diff --git a/SwiftProject/SwiftProject.xcodeproj/project.pbxproj b/SwiftProject/SwiftProject.xcodeproj/project.pbxproj index e42160e..eaaebdf 100644 --- a/SwiftProject/SwiftProject.xcodeproj/project.pbxproj +++ b/SwiftProject/SwiftProject.xcodeproj/project.pbxproj @@ -20,6 +20,9 @@ 006B61D32BBAA938003FCB49 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 006B61D22BBAA938003FCB49 /* StoreKit.framework */; }; 006B61DC2BBCFAC4003FCB49 /* CustomSheetController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006B61DB2BBCFAC4003FCB49 /* CustomSheetController.swift */; }; 006B61DE2BBCFB45003FCB49 /* CustomSheetCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006B61DD2BBCFB45003FCB49 /* CustomSheetCell.swift */; }; + 00733EA92BFB462500D53BA8 /* CCSpatialShootController+SessionConfigure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00733EA82BFB462500D53BA8 /* CCSpatialShootController+SessionConfigure.swift */; }; + 00733EAB2BFB471100D53BA8 /* CCSpatialShootController+CaputreAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00733EAA2BFB471100D53BA8 /* CCSpatialShootController+CaputreAction.swift */; }; + 00733EAD2BFB47AE00D53BA8 /* CCSpatialShootController+Generate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00733EAC2BFB47AE00D53BA8 /* CCSpatialShootController+Generate.swift */; }; 0073BD142BCE80F700721885 /* ZZHCustomPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0073BD132BCE80F700721885 /* ZZHCustomPlayer.swift */; }; 0073BD182BCF7B3400721885 /* ZZHCustomSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0073BD172BCF7B3400721885 /* ZZHCustomSlider.swift */; }; 0073BD1A2BCFC8E800721885 /* ZZHCustomPlayerForVideoTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0073BD192BCFC8E800721885 /* ZZHCustomPlayerForVideoTask.swift */; }; @@ -135,6 +138,9 @@ 006B61DD2BBCFB45003FCB49 /* CustomSheetCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomSheetCell.swift; sourceTree = ""; }; 0072361E2BD13B9D000595A9 /* ZZHCustomVideoCompositor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZZHCustomVideoCompositor.swift; sourceTree = ""; }; 007236202BD13C75000595A9 /* ZZHCustomVideoCompositionInstruction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZZHCustomVideoCompositionInstruction.swift; sourceTree = ""; }; + 00733EA82BFB462500D53BA8 /* CCSpatialShootController+SessionConfigure.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CCSpatialShootController+SessionConfigure.swift"; sourceTree = ""; }; + 00733EAA2BFB471100D53BA8 /* CCSpatialShootController+CaputreAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CCSpatialShootController+CaputreAction.swift"; sourceTree = ""; }; + 00733EAC2BFB47AE00D53BA8 /* CCSpatialShootController+Generate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CCSpatialShootController+Generate.swift"; sourceTree = ""; }; 0073BD132BCE80F700721885 /* ZZHCustomPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZZHCustomPlayer.swift; sourceTree = ""; }; 0073BD172BCF7B3400721885 /* ZZHCustomSlider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZZHCustomSlider.swift; sourceTree = ""; }; 0073BD192BCFC8E800721885 /* ZZHCustomPlayerForVideoTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZZHCustomPlayerForVideoTask.swift; sourceTree = ""; }; @@ -309,6 +315,17 @@ path = deapt; sourceTree = ""; }; + 00733EA72BFB460C00D53BA8 /* CCSpatialShootController */ = { + isa = PBXGroup; + children = ( + 1EE5C5F62B8F973A00EDFC2F /* CCSpatialShootController.swift */, + 00733EA82BFB462500D53BA8 /* CCSpatialShootController+SessionConfigure.swift */, + 00733EAA2BFB471100D53BA8 /* CCSpatialShootController+CaputreAction.swift */, + 00733EAC2BFB47AE00D53BA8 /* CCSpatialShootController+Generate.swift */, + ); + path = CCSpatialShootController; + sourceTree = ""; + }; 0096624B2BB3BA0100FCA65F /* ExternalScreen */ = { isa = PBXGroup; children = ( @@ -380,7 +397,7 @@ children = ( 009B28AE2BC2692F006D8B8E /* CCSpatialPhotoDisplayController */, 009662582BB5527200FCA65F /* CCSpatialVideoDisplayController */, - 1EE5C5F62B8F973A00EDFC2F /* CCSpatialShootController.swift */, + 00733EA72BFB460C00D53BA8 /* CCSpatialShootController */, 1E39459F2B8398B000D0F5CA /* VRVideoTransformController.swift */, 1EFAF0BF2B8B7A59002A1773 /* VRPhotoTransformController.swift */, ); @@ -872,6 +889,7 @@ AF2120E62B4E9DE000400B7F /* CCTableSwitchView.swift in Sources */, 0096624D2BB3BA3B00FCA65F /* ZZHExternalViewController.swift in Sources */, 00D33BFA2B9AB21A00604A44 /* ZZHAVExtension.swift in Sources */, + 00733EAD2BFB47AE00D53BA8 /* CCSpatialShootController+Generate.swift in Sources */, 009661F82BAD6C7100FCA65F /* CCSpaceAlbumFilterPopView2.swift in Sources */, 009DFB132BC8EA90007B56E8 /* MenuVCCell.swift in Sources */, AF2120E02B4E9C8000400B7F /* Timer+Add.swift in Sources */, @@ -883,6 +901,7 @@ AFD9F5952B58D029008716DE /* MetalPlayer.swift in Sources */, AF2121002B4EA5FE00400B7F /* CCRegisterController.swift in Sources */, AF2120D02B4E982300400B7F /* CCUserDefault.swift in Sources */, + 00733EA92BFB462500D53BA8 /* CCSpatialShootController+SessionConfigure.swift in Sources */, AF2120EC2B4EA2FC00400B7F /* BaseController.swift in Sources */, AF2120D22B4E99E600400B7F /* CCAddImage.swift in Sources */, AF2120E42B4E9D5A00400B7F /* CCSMSView.swift in Sources */, @@ -905,6 +924,7 @@ AF2120FE2B4EA5F100400B7F /* CCLoginController.swift in Sources */, 006B61CF2BBA5D0A003FCB49 /* MembershipVCConfigureView.swift in Sources */, AF2120CE2B4E979500400B7F /* CCTextField.swift in Sources */, + 00733EAB2BFB471100D53BA8 /* CCSpatialShootController+CaputreAction.swift in Sources */, 1E1EA2882B9325D300A5D5D2 /* CCSpatialPhotoDisplayController.swift in Sources */, 006B61DC2BBCFAC4003FCB49 /* CustomSheetController.swift in Sources */, ); diff --git a/SwiftProject/SwiftProject.xcworkspace/xcuserdata/aaa.xcuserdatad/UserInterfaceState.xcuserstate b/SwiftProject/SwiftProject.xcworkspace/xcuserdata/aaa.xcuserdatad/UserInterfaceState.xcuserstate index 648f3c8..1128009 100644 Binary files a/SwiftProject/SwiftProject.xcworkspace/xcuserdata/aaa.xcuserdatad/UserInterfaceState.xcuserstate and b/SwiftProject/SwiftProject.xcworkspace/xcuserdata/aaa.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/SwiftProject/SwiftProject.xcworkspace/xcuserdata/aaa.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/SwiftProject/SwiftProject.xcworkspace/xcuserdata/aaa.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index abf744f..dcc11bb 100644 --- a/SwiftProject/SwiftProject.xcworkspace/xcuserdata/aaa.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/SwiftProject/SwiftProject.xcworkspace/xcuserdata/aaa.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -50,38 +50,6 @@ endingLineNumber = "141" landmarkName = "init(menuWidth:arrow:datas:configures:dissMissCallback:)" landmarkType = "7"> - - - - - - - - - - - - - - - - - - - - - - - - @@ -486,39 +358,7 @@ startingLineNumber = "169" endingLineNumber = "169" landmarkName = "show(praiseCallback:negativeCallback:)" - landmarkType = "9"> - - - - - - + landmarkType = "7"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialShootController.swift b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialShootController.swift deleted file mode 100644 index 26fd18c..0000000 --- a/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialShootController.swift +++ /dev/null @@ -1,1288 +0,0 @@ -// -// CCSpatialShootController.swift -// SwiftProject -// -// Created by Zhang, Joyce on 2024/2/29. -// - -import UIKit -import AVFoundation -import Photos -import AVKit -import VideoToolbox -import SVProgressHUD -import Firebase -import CoreMotion -class CCSpatialShootController: BaseController { - let kNowTimeToUserDefaultKey_SpatialShootController:String = "kNowTimeToUserDefaultKey_SpatialShootController" - var wideAngleCameraDevice:AVCaptureDevice?//测试使用 - - //AVCaptureSession --- 单摄像头 - var session = AVCaptureMultiCamSession()//多摄像头 - var wideAngleCameraDeviceInput: AVCaptureDeviceInput?//广角摄像头 .builtInWideAngleCamera - var ultraWideCameraDeviceInput: AVCaptureDeviceInput?//超广角 .builtInUltraWideCamera - - - var wideAngleCameraVideoPreviewLayer: AVCaptureVideoPreviewLayer?//广角摄像头 - var ultraWideCameraVideoPreviewLayer: AVCaptureVideoPreviewLayer?//超广角 - - var wideAngleCameraMovieOutput: AVCaptureMovieFileOutput? - var ultraWideCameraMovieOutput: AVCaptureMovieFileOutput? - - - - var isRecording = false - var imageCGImagePropertyOrientation:CGImagePropertyOrientation = CGImagePropertyOrientation.left//由于cgimage无法保留图像的方向 - var deviceOrientation = 1//监听设备方向 1,表示竖屏;2,表示横屏 - - var leftEyeVideoURL:URL? - var rightEyeVideoURL:URL? - var outputVideoURL: URL? - let motionManager = CMMotionManager() - var imgs:NSMutableArray = NSMutableArray() //存放广角、主摄照片数组 - let spatialVideoWriter = SpatialVideoWriter() - //================================ - - //当前相机模式 - var shootingMode:CCShootingMode = .CCShootingMode_Camera - - //记录拍摄时长 - var timer: Timer? - - lazy var spaceAlbumPopView: CCSpaceAlbumFilterPopView2 = { - let view = CCSpaceAlbumFilterPopView2.init(frame: CGRectMake(0, 0, KScreenWidth, KScreenHeight)) - return view - }() - - lazy var tipsLabel: UILabel = { - let label = UILabel.init(frame: CGRectMake((KScreenWidth - 320)/2, KNavigationBarHeight+KStatusBarHeight + 20, 320, 36)) -// label.backgroundColor = UIColor(hexString: "ffffff", alpha: 0.2) - label.backgroundColor = UIColor.darkGray - label.font = KFont_Medium(12) - label.textColor = KTextColor_White - label.text = ""//你只需捕捉美好,转码交给我们" - label.textAlignment = .center - label.alpha = 1.0 - label.layer.cornerRadius = 8 - label.clipsToBounds = true - label.isHidden = true - return label - }() - - lazy var horizontalImageView: UIImageView = { - let imageView = UIImageView() - imageView.image = UIImage.init(named: "horizontal_rotate_img") - // imageView.backgroundColor = .green - imageView.alpha = 0.0 - imageView.isHidden = true - return imageView - }() - - lazy var effectView: UIVisualEffectView = { - let effect = UIBlurEffect(style: .light) - let view = UIVisualEffectView.init(effect: effect) - view.backgroundColor = .clear - view.alpha = 0.0 - view.isHidden = true - return view - }() - - - lazy var horizontalLabel: UILabel = { - let label = UILabel() - label.backgroundColor = UIColor.clear - label.font = KFont_Medium(14) - label.textColor = KTextColor_White - label.text = NSLocalizedString("将iPhone旋转为横向", comment: "") - label.alpha = 0.0 - label.isHidden = true - return label - }() - - lazy var shutterRingView: UIView = { - let view = UIView() - view.frame = CGRect(x:0,y:0,width:66,height:66) - view.backgroundColor = .clear - view.layer.cornerRadius = 33 - view.layer.masksToBounds = true - view.layer.borderWidth = 3 - view.layer.borderColor = UIColor.white.cgColor - view.center = CGPointMake(KScreenWidth/2, KScreenHeight - KTabbarSafeBottomMargin - 50 - 33) - return view - }() - - lazy var shutterPhotoButton: UIButton = { - let botton = UIButton() - botton.frame = CGRect(x:0,y:0,width:56,height:56) - botton.backgroundColor = .white - // botton.setTitle("开始", for: .normal) - // botton.setTitle("录制中", for: .selected) - botton.layer.cornerRadius = 28 - botton.layer.masksToBounds = true - botton.center = CGPointMake(33, 33) - // botton.layer.position = CGPoint(x:self.view.bounds.width/2 - 70,y:self.view.bounds.height-50) - botton.addTarget(self, action: #selector(shutterPhotoButtonAction), for: .touchUpInside) - return botton - }() - - lazy var shutterVideoButton: UIButton = { - let botton = UIButton() - botton.frame = CGRect(x:0,y:0,width:56,height:56) - botton.backgroundColor = UIColor(hexString: "#FF3B2F") - // botton.setTitle("开始", for: .normal) - // botton.setTitle("录制中", for: .selected) - botton.layer.cornerRadius = 28 - botton.layer.masksToBounds = true - botton.center = CGPointMake(33, 33) - // botton.layer.position = CGPoint(x:self.view.bounds.width/2 - 70,y:self.view.bounds.height-50) - botton.addTarget(self, action: #selector(shutterVideoButtonAction(_:)), for: .touchUpInside) - botton.isHidden = true - return botton - }() - - - - - lazy var albumButton: UIButton = { - let botton = UIButton() - botton.frame = CGRect(x:0,y:0,width:48,height:48) - - // botton.backgroundColor = UIColor(hexString: "#FF3B2F") - // botton.setTitle("开始", for: .normal) - // botton.setTitle("录制中", for: .selected) - botton.layer.cornerRadius = 8 - botton.layer.masksToBounds = true - botton.center = CGPointMake(shutterRingView.center.x - 92 - 24, shutterRingView.center.y) - // botton.layer.position = CGPoint(x:self.view.bounds.width/2 - 70,y:self.view.bounds.height-50) - botton.addTarget(self, action: #selector(albumButtonAction), for: .touchUpInside) - botton.contentMode = .scaleAspectFill - return botton - }() - - - lazy var lightButton: UIButton = { - let botton = UIButton() - botton.frame = CGRect(x:0,y:0,width:40,height:40) - - // botton.backgroundColor = UIColor(hexString: "#FF3B2F") - botton.setImage(UIImage.init(named: "light_button_normal"), for: .normal) - - // botton.setTitle("开始", for: .normal) - // botton.setTitle("录制中", for: .selected) - // botton.layer.cornerRadius = 8 - // botton.layer.masksToBounds = true - botton.center = CGPointMake( shutterRingView.center.x + 103 + 20, shutterRingView.center.y) - // botton.layer.position = CGPoint(x:self.view.bounds.width/2 - 70,y:self.view.bounds.height-50) - botton.addTarget(self, action: #selector(lightButtonAction), for: .touchUpInside) - return botton - }() - - - lazy var switchBackView: UIView = { - let view = UIView() - view.backgroundColor = UIColor(hexString: "#ffffff", alpha: 0.6) - view.layer.cornerRadius = 20 - view.layer.masksToBounds = true - // view.layer.borderWidth = 3 - // view.layer.borderColor = UIColor.white.cgColor - // view.center = CGPointMake(KScreenWidth/2, KScreenHeight - KTabbarSafeBottomMargin - 50 - 33) - return view - }() - - lazy var vipLogoView:UIImageView = { - let imgView = UIImageView() - imgView.image = UIImage(named: "camera_vip") - return imgView - }() - - lazy var cameraButton: UIButton = { - let botton = UIButton() - botton.tag = 1001 - // botton.frame = CGRect(x:0,y:0,width:40,height:40) - - botton.backgroundColor = UIColor(hexString: "#000000", alpha: 0.3) - botton.setImage(UIImage(named: "camera_button_selected"), for: .normal) - // botton.setImage(UIImage(named: "camera_button_selected"), for: .selected) - // botton.setTitle("开始", for: .normal) - // botton.setTitle("录制中", for: .selected) - botton.layer.cornerRadius = 16 - botton.layer.masksToBounds = true - // botton.center = CGPointMake( shutterRingView.center.x + 103 + 20, shutterRingView.center.y) - // botton.layer.position = CGPoint(x:self.view.bounds.width/2 - 70,y:self.view.bounds.height-50) - botton.addTarget(self, action: #selector(switchButtonAction(_:)), for: .touchUpInside) - return botton - }() - - lazy var videoButton: UIButton = { - let botton = UIButton() - botton.tag = 1002 - // botton.frame = CGRect(x:0,y:0,width:40,height:40) - - botton.backgroundColor = .clear - botton.setImage(UIImage(named: "video_button_normal"), for: .normal) - // botton.setImage(UIImage(named: "camera_button_selected"), for: .selected) - // botton.setTitle("开始", for: .normal) - // botton.setTitle("录制中", for: .selected) - botton.layer.cornerRadius = 16 - botton.layer.masksToBounds = true - // botton.center = CGPointMake( shutterRingView.center.x + 103 + 20, shutterRingView.center.y) - // botton.layer.position = CGPoint(x:self.view.bounds.width/2 - 70,y:self.view.bounds.height-50) - botton.addTarget(self, action: #selector(switchButtonAction(_:)), for: .touchUpInside) - return botton - }() - - lazy var cameraLabel: UILabel = { - let label = UILabel() - label.backgroundColor = UIColor.clear - label.font = KFont_Medium(12) - label.textColor = UIColor(hexString: "#ffffff", alpha: 0.6) - label.text = NSLocalizedString("拍照", comment: "") - label.isHidden = false - return label - }() - - lazy var videoLabel: UILabel = { - let label = UILabel() - label.backgroundColor = UIColor.clear - label.font = KFont_Medium(12) - label.textColor = UIColor(hexString: "#ffffff", alpha: 0.6) - label.text = NSLocalizedString("摄像", comment: "") - label.isHidden = true - return label - }() - - //================================= - //MARK: - viewWillAppear - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - - - self.setNavgationBarColor(color: UIColor.clear) - self.setNavgationBarLine(color: .clear) - } - - - override func viewDidDisappear(_ animated: Bool) { - super.viewDidDisappear(animated) - let sec:TimeInterval = ZZHHelper.getSecFromUserDefaultByKey(kNowTimeToUserDefaultKey_SpatialShootController) - Analytics.logEvent("capture_pv", parameters: ["refer_page":"相机拍摄页面","duration":sec]) - } - - override func viewDidLoad() { - super.viewDidLoad() - ZZHHelper.setNowTimeToUserDefaultWithKey(kNowTimeToUserDefaultKey_SpatialShootController) - - outputVideoURL = URL.documentsDirectory.appendingPathComponent("output.MOV") - configureSession() // 设置相机参数 - - setUI() - self.view.bringSubviewToFront(navtionImgView!) - - //判断当前是否是横屏 - let isLandscape = UIDevice.current.orientation.isLandscape - if !isLandscape { - showHorizontalScreenTips() - } - - if motionManager.isDeviceMotionAvailable { - motionManager.startDeviceMotionUpdates(to: OperationQueue.main) {[weak self] motion, error in - if let motion = motion { - - // 处理设备方向变化 - self?.dealDeviceScreenOrientationByMotaion(motion) - } - } - } - - - - //读取相册第一张照片 - getAlbumFirstPhoto() - enableShutterBtn(false) - //进入页面的首选项调整为视频拍摄 - switchButtonAction(videoButton) - } - - private func setUI() { - - self.view.addSubview(effectView) - self.view.addSubview(horizontalImageView) - self.view.addSubview(horizontalLabel) - self.view.bringSubviewToFront(horizontalImageView) - self.view.bringSubviewToFront(horizontalLabel) - - //快门按钮 - self.view.addSubview(shutterRingView) - - shutterRingView.addSubview(shutterPhotoButton) - shutterRingView.addSubview(shutterVideoButton) - - self.view.addSubview(albumButton) - self.view.addSubview(lightButton) - - self.view.addSubview(switchBackView) - switchBackView.addSubview(cameraButton) - - switchBackView.addSubview(videoButton) - if !UserInfo.sharedInstance.isMemberShip { - self.view.addSubview(vipLogoView) - } - - - self.view.addSubview(cameraLabel) - self.view.addSubview(videoLabel) - self.setRightOneBtnTitle(string: "4K·3D") - - effectView.snp.makeConstraints { (make) in - make.top.leading.bottom.trailing.equalTo(self.view) - } - - horizontalImageView.snp.makeConstraints { (make) in - make.centerY.equalTo(self.view.snp.centerY) - make.centerX.equalTo(self.view.snp.centerX) - make.width.equalTo(28) - make.height.equalTo(28) - } - - horizontalLabel.snp.makeConstraints { (make) in - make.top.equalTo(self.horizontalImageView.snp.bottom).offset(16) - make.centerX.equalTo(self.view.snp.centerX) - } - - switchBackView.snp.makeConstraints { (make) in - make.bottom.equalTo(shutterRingView.snp.top).offset(-32) - make.centerX.equalTo(self.view.snp.centerX) - make.width.equalTo(128) - make.height.equalTo(40) - } - if !UserInfo.sharedInstance.isMemberShip { - vipLogoView.snp.makeConstraints { make in - make.width.equalTo(25) - make.height.equalTo(12) - make.left.equalTo(switchBackView.snp.right).offset(-22) - make.bottom.equalTo(switchBackView.snp.top).offset(12) - } - } - - - - cameraButton.snp.makeConstraints { (make) in - make.centerY.equalTo(switchBackView.snp.centerY) - - make.trailing.equalTo(switchBackView.snp.trailing).offset(-4) - make.width.equalTo(56) - make.height.equalTo(32) - } - - - - videoButton.snp.makeConstraints { (make) in - make.centerY.equalTo(switchBackView.snp.centerY) - make.leading.equalTo(switchBackView.snp.leading).offset(4) - make.width.equalTo(56) - make.height.equalTo(32) - } - - cameraLabel.snp.makeConstraints { (make) in - make.bottom.equalTo(switchBackView.snp.top).offset(-8) - - make.centerX.equalTo(switchBackView.snp.trailing).offset(-32) - } - - videoLabel.snp.makeConstraints { (make) in - make.bottom.equalTo(switchBackView.snp.top).offset(-8) - make.centerX.equalTo(switchBackView.snp.leading).offset(32) - } - } - - - private func configureSession() { - session.beginConfiguration() - defer { - session.commitConfiguration() - } - - // 配置后置摄像头 .builtInWideAngleCamera 广角摄像头(默认设备 --- 主摄,28mm左右焦段) .back 后置摄像头 - guard let backCamera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else { - print("Could not find the back camera") - return - } - wideAngleCameraDevice = backCamera - - do { - wideAngleCameraDeviceInput = try AVCaptureDeviceInput(device: backCamera) - - guard let wideAngleCameraDeviceInput = wideAngleCameraDeviceInput, - session.canAddInput(wideAngleCameraDeviceInput) else { - print("Could not add back camera input") - return - } - session.addInput(wideAngleCameraDeviceInput) - } catch { - print("Could not create back camera device input: \(error)") - return - } - - // .builtInUltraWideCamera 超广角(默认设备的0.5x,只能使用AVCaptureDeviceDiscoverySession获取) .back 后置摄像头 - guard let frontCamera = AVCaptureDevice.default(.builtInUltraWideCamera, for: .video, position: .back) else { - print("Could not find the front camera") - return - } - - do { - print("builtInUltraWideCamera:\(frontCamera.videoZoomFactor)") - - // 设置焦距 -// frontCamera.focusMode = .autoFocus - ultraWideCameraDeviceInput = try AVCaptureDeviceInput(device: frontCamera) - guard let ultraWideCameraDeviceInput = ultraWideCameraDeviceInput, - session.canAddInput(ultraWideCameraDeviceInput) else { - print("Could not add front camera input") - return - } - session.addInput(ultraWideCameraDeviceInput) - } catch { - print("Could not create front camera device input: \(error)") - return - } - - // 配置音频输入设备 - guard let audioDevice = AVCaptureDevice.default(for: .audio) else { - print("Could not find audio device") - return - } - - do { - let audioDeviceInput = try AVCaptureDeviceInput(device: audioDevice) - - guard session.canAddInput(audioDeviceInput) else { - print("Could not add audio input") - return - } - - session.addInput(audioDeviceInput) - } catch { - print("Could not create audio device input: \(error)") - return - } - - // 配置后置摄像头输出 - wideAngleCameraMovieOutput = AVCaptureMovieFileOutput() - guard let wideAngleCameraMovieOutput = wideAngleCameraMovieOutput, - session.canAddOutput(wideAngleCameraMovieOutput) else { - print("Could not add the back camera movie output") - return - } - session.addOutput(wideAngleCameraMovieOutput) - - // 配置前置摄像头输出 - ultraWideCameraMovieOutput = AVCaptureMovieFileOutput() - guard let ultraWideCameraMovieOutput = ultraWideCameraMovieOutput, - session.canAddOutput(ultraWideCameraMovieOutput) else { - print("Could not add the front camera movie output") - return - } - session.addOutput(ultraWideCameraMovieOutput) - - // 配置预览图层 --- 主摄 - wideAngleCameraVideoPreviewLayer = AVCaptureVideoPreviewLayer(session: session) -// wideAngleCameraVideoPreviewLayer?.frame = CGRect(x: 0, y: 0, width: view.frame.size.width / 2, height: view.frame.size.height) - wideAngleCameraVideoPreviewLayer?.frame = CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height) - if let wideAngleCameraVideoPreviewLayer = wideAngleCameraVideoPreviewLayer { - wideAngleCameraVideoPreviewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill - // view.layer.addSublayer(wideAngleCameraVideoPreviewLayer) - self.view.layer.insertSublayer(wideAngleCameraVideoPreviewLayer, at: 0) - } - //广角 --- 这里隐藏广角视频界面,只展示一个主摄的界面 -// ultraWideCameraVideoPreviewLayer = AVCaptureVideoPreviewLayer(session: session) -// ultraWideCameraVideoPreviewLayer?.frame = CGRect(x: view.frame.size.width / 2, y: 0, width: view.frame.size.width / 2, height: view.frame.size.height) -// if let ultraWideCameraVideoPreviewLayer = ultraWideCameraVideoPreviewLayer { -// ultraWideCameraVideoPreviewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill -// // view.layer.addSublayer(ultraWideCameraVideoPreviewLayer) -// self.view.layer.insertSublayer(ultraWideCameraVideoPreviewLayer, at: 0) -// } - DispatchQueue.global().async { - self.session.startRunning() - - //变焦设置需要在AVCaptureMultiCamSession statrunning之后设置才会生效 - try! frontCamera.lockForConfiguration() - frontCamera.videoZoomFactor = 1.75//变焦 - frontCamera.unlockForConfiguration() - - } - } - - - - - func showHorizontalScreenTips() { - self.horizontalImageView.isHidden = false - self.horizontalLabel.isHidden = false - - //模糊遮罩 - self.effectView.isHidden = false - - UIView.animate(withDuration: 0.5) { - self.horizontalImageView.alpha = 1.0 - self.horizontalLabel.alpha = 1.0 - //模糊遮罩 - self.effectView.alpha = 1.0 - - } - - } - - func hidenHorizontalScreenTips() { - UIView.animate(withDuration: 0.5) { - self.horizontalImageView.alpha = 0.0 - self.horizontalLabel.alpha = 0.0 - //模糊遮罩 - self.effectView.alpha = 0.0 - - }completion: { finished in - self.horizontalImageView.isHidden = true - self.horizontalLabel.isHidden = true - //模糊遮罩 - self.effectView.isHidden = true - - } - - } - - - //MARK: - action - @objc func albumButtonAction() { - // let vc:CCSpaceAlbumController = CCSpaceAlbumController() - // self.present(vc, animated: true, completion: nil) - self.view.addSubview(spaceAlbumPopView) - spaceAlbumPopView.show() - - //选择图片 - spaceAlbumPopView.selectedImageHandler = { [self] data,asset in - print("回调") - - let vc = CCSpatialPhotoDisplayController() - vc.photoOriginalData = data - vc.imageAsset = asset - - self.navigationController?.pushViewController(vc, animated: true) - } - //选择视频 - spaceAlbumPopView.selectedVideoHandler = { url,asset in - if (asset.duration > 60 ) { - if !UserInfo.sharedInstance.isMemberShip { - let vc:MembershipVC = MembershipVC() - let nav = UINavigationController(rootViewController: vc) - nav.modalTransitionStyle = UIModalTransitionStyle.coverVertical - nav.modalPresentationStyle = .fullScreen - self.present(nav, animated: true) - return - } - } - let vc = CCSpatialVideoDisplayController() - vc.sourceVideoURL = url - vc.videoOriginalPHAsset = asset - self.navigationController?.pushViewController(vc, animated: true) - } - - } - - - - - @objc func lightButtonAction() { - - guard let device = AVCaptureDevice.default(for: .video) else { - print("无法获取到您的设备") - return - } - if device.hasTorch && device.isTorchAvailable{ - try? device.lockForConfiguration() - - device.torchMode = device.torchMode == .off ? .on : .off - - device.unlockForConfiguration() - } - } - - @objc func switchButtonAction(_ sender: UIButton){ - if sender.tag == 1001 { - if UserInfo.sharedInstance.isMemberShip { - //拍照 - shootingMode = .CCShootingMode_Camera - - shutterPhotoButton.isHidden = false - shutterVideoButton.isHidden = true - changeSwitchstatus() - } - else {//前往内购订阅页面 - let vc:MembershipVC = MembershipVC() - let nav = UINavigationController(rootViewController: vc) - nav.modalTransitionStyle = UIModalTransitionStyle.coverVertical - nav.modalPresentationStyle = .fullScreen - self.present(nav, animated: true) - //统计 - Analytics.logEvent("capture_unvip_pv", parameters: nil) - - } - - }else if sender.tag == 1002 { - //录像 - shootingMode = .CCShootingMode_Video - - shutterPhotoButton.isHidden = true - shutterVideoButton.isHidden = false - changeSwitchstatus() - } - - - } - - - //拍照 - @objc func shutterPhotoButtonAction(){ - - - shutterPhotoButton.backgroundColor = .gray - - imgs.removeAllObjects() - guard let wideAngleCameraMovieOutput = wideAngleCameraMovieOutput, - let ultraWideCameraMovieOutput = ultraWideCameraMovieOutput else { - print("Movie output not configured") - return - } - - //快门声音 - let soundID: SystemSoundID = 1108 - AudioServicesPlaySystemSound(soundID) - - let time = Date().timeIntervalSince1970 - let name1 = "wideAnglePhoto" + String(time) + ".mov" - let name2 = "ultraWidePhoto" + String(time) + ".mov" - let wideAngleCameraOutputURL = URL.documentsDirectory.appending(path:name1) - let ultraWideCameraOutputURL = URL.documentsDirectory.appending(path:name2) - - wideAngleCameraMovieOutput.startRecording(to: wideAngleCameraOutputURL, recordingDelegate: self) - ultraWideCameraMovieOutput.startRecording(to: ultraWideCameraOutputURL, recordingDelegate: self) - - //摄像头录像,取0.1秒长度,如果是拍摄照片去两张,拍摄成功会听到两次声音,所以这里用摄像的方式取图片 - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - self.shutterPhotoButton.backgroundColor = .white - self.stopVideoRecording() - } - } - - //录像 - @objc func shutterVideoButtonAction(_ sender: UIButton){ - if !self.isRecording { - //判断设备方向,如果不是横屏left\right,则不允许录制 -// if imageOrientation != .right || -// imageOrientation != .left { -// return -// } - - //录像中 - print("录像中...") - - UIView.animate(withDuration: 0.3) { - // 更新视图的transform属性,将其缩小 - self.shutterVideoButton.transform = CGAffineTransform(scaleX: 0.53, y: 0.53) - self.shutterVideoButton.layer.cornerRadius = 4 - }completion: { finished in - self.isRecording = true - self.changeSwitchstatus() - } - - - //开启录制 - startRecordingTimer() - startVideoRecording() - - - }else{ - stopRVideo() - } - } - - func stopRVideo(){ - print("录像结束!!!") - //停止录制 - stopRecordingTimer() - stopVideoRecording() - UIView.animate(withDuration: 0.3) { - // 更新视图的transform属性,将其放大 - self.shutterVideoButton.transform = CGAffineTransform(scaleX: 1.0, y: 1.0) - self.shutterVideoButton.layer.cornerRadius = 28 - }completion: { finished in - self.isRecording = false - self.changeSwitchstatus() - } - } - - // MARK: - 开始/停止录制 - private func startVideoRecording() { - guard let wideAngleCameraMovieOutput = wideAngleCameraMovieOutput, - let ultraWideCameraMovieOutput = ultraWideCameraMovieOutput else { - print("Movie output not configured") - return - } - - let time = Date().timeIntervalSince1970 - let name1 = "wideAngleVideo" + String(time) + ".mov" - let name2 = "ultraWideVideo" + String(time) + ".mov" - leftEyeVideoURL = nil - rightEyeVideoURL = nil - let wideAngleCameraOutputURL = URL.documentsDirectory.appending(path:name1) - let fultraWideCameraOutputURL = URL.documentsDirectory.appending(path:name2) - - wideAngleCameraMovieOutput.startRecording(to: wideAngleCameraOutputURL, recordingDelegate: self) - ultraWideCameraMovieOutput.startRecording(to: fultraWideCameraOutputURL, recordingDelegate: self) - - - } - - private func stopVideoRecording(){ - guard let wideAngleCameraMovieOutput = wideAngleCameraMovieOutput, - let ultraWideCameraMovieOutput = ultraWideCameraMovieOutput else { - print("Movie output not configured") - return - } - - wideAngleCameraMovieOutput.stopRecording() - ultraWideCameraMovieOutput.stopRecording() - - } - - //MARK: - 改变按钮状态 - func changeSwitchstatus() { - - if isRecording { - self.switchBackView.isHidden = true - self.vipLogoView.isHidden = true - cameraLabel.isHidden = true - videoLabel.isHidden = true - }else{ - self.switchBackView.isHidden = false - if !UserInfo.sharedInstance.isMemberShip { - self.vipLogoView.isHidden = false - } - - if shootingMode == .CCShootingMode_Camera { - //拍照 - cameraLabel.isHidden = false - videoLabel.isHidden = true - - cameraButton.backgroundColor = UIColor(hexString: "#000000", alpha: 0.3) - cameraButton.setImage(UIImage(named: "camera_button_selected"), for: .normal) - - videoButton.backgroundColor = .clear - videoButton.setImage(UIImage(named: "video_button_normal"), for: .normal) - - }else if shootingMode == .CCShootingMode_Video { - //录像 - cameraLabel.isHidden = true - videoLabel.isHidden = false - - cameraButton.backgroundColor = .clear - cameraButton.setImage(UIImage(named: "camera_button_normal"), for: .normal) - - videoButton.backgroundColor = UIColor(hexString: "#000000", alpha: 0.3) - videoButton.setImage(UIImage(named: "video_button_selected"), for: .normal) - } - } - } - - - // MARK: - 开启/关闭定时器 - func startRecordingTimer() { - var count = 0 // 初始化计数变量为0 - timer = Timer(timeInterval: 1, repeats: true, block: { timer in - DispatchQueue.global().async { - // 在后台线程中执行任务 - // 模拟耗时操作(这里只是打印当前计数) - count += 1 - let timeText = self.transToHourMinSec(second: count) - DispatchQueue.main.async { -// self.setNavgaionTitle(string: timeText) - self.setNavgationTitleForCaptureVC(string: timeText) - } - } - }) - - //录制过程中timer会被暂停,只有加入到runloop中才能执行 - RunLoop.main.add(timer!, forMode: .common) - } - - func stopRecordingTimer() { - self.setNavgaionTitle(string: "") - if let timer = self.timer { - // 当视图控制器被释放时,取消定时器 - timer.invalidate() - } - } - - - - // MARK: - 把秒数转换成时分秒(00:00:00)格式 - /// - /// - Parameter second: 秒数 - /// - Returns: String - func transToHourMinSec(second: Int) -> String { - let allTime: Int = second - var hours = 0 - var minutes = 0 - var seconds = 0 - var hoursText = "" - var minutesText = "" - var secondsText = "" - - hours = allTime / 3600 - hoursText = hours > 9 ? "\(hours)" : "0\(hours)" - - minutes = allTime % 3600 / 60 - minutesText = minutes > 9 ? "\(minutes)" : "0\(minutes)" - - seconds = allTime % 3600 % 60 - secondsText = seconds > 9 ? "\(seconds)" : "0\(seconds)" - - return "\(hoursText):\(minutesText):\(secondsText)" - } - - - //MARK: - 获取相册第一张图片 - func getAlbumFirstPhoto() { - PHPhotoLibrary.requestAuthorization(for: .readWrite) {[weak self] (status) in - if status == PHAuthorizationStatus.authorized { - let fetchOptions = PHFetchOptions() - fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: false)] - - let image_assetsFetchResults: PHFetchResult! = PHAsset.fetchAssets(with: .image, options: fetchOptions) - - let video_assetsFetchResults: PHFetchResult! = PHAsset.fetchAssets(with: .video, options: fetchOptions) - - let imgAsset = image_assetsFetchResults.firstObject - let videoAsset = video_assetsFetchResults.firstObject - var defaultAsset:PHAsset? - if let imgAsset { - defaultAsset = imgAsset - } - - if let videoAsset{ - if let temp = defaultAsset { - if (videoAsset.creationDate ?? Date.now > imgAsset?.creationDate ?? Date.now) { - defaultAsset = videoAsset - } - } - else { - defaultAsset = videoAsset - } - } - - if let defaultAsset { - self?.getImageFromAsset(asset: defaultAsset) - } - - - -// if let asset = image_assetsFetchResults.firstObject { -// let imageManager = PHCachingImageManager() -// -// let requestOptions = PHImageRequestOptions() -// requestOptions.isSynchronous = true -// requestOptions.deliveryMode = .highQualityFormat -// -// imageManager.requestImageDataAndOrientation(for: asset, options: requestOptions) { imageData, dataUTI, imagePropertyOrientation, info in -// if let imageData = imageData { -// DispatchQueue.main.async { -// self.albumButton.setBackgroundImage(UIImage(data: imageData), for: .normal) -// } -// } -// else { -// print("没有获取到所旅途.....") -// } -// } -// } -// else { -// print("22222没有获取到所旅途.....") -// } - } - } - } - - func getImageFromAsset(asset:PHAsset){ - //处理模型 - let requestOptions = PHImageRequestOptions() - requestOptions.isSynchronous = false//设置成同步回调 - requestOptions.deliveryMode = .highQualityFormat - PHImageManager.default().requestImageDataAndOrientation(for: asset, options: requestOptions) {[weak self] data, dataUTI, imagePropertyOrientation, info in - if let hasData = data { - DispatchQueue.main.async { - self?.albumButton.setBackgroundImage(UIImage(data: hasData), for: .normal) - } - } - - } - } - - - func enableShutterBtn(_ value:Bool) { - shutterPhotoButton.isEnabled = value - shutterVideoButton.isEnabled = value - } - - //MARK: - 屏幕旋转监听 - func dealDeviceScreenOrientationByMotaion(_ motion:CMDeviceMotion){ - - let x = motion.gravity.x - let y = motion.gravity.y - - if (fabs(y) >= fabs(x)){ - - if deviceOrientation != 1 { - enableShutterBtn(false) - deviceOrientation = 1 - print("竖屏"); - autoStopRecordVideo() - showHorizontalScreenTips() - } - - } - else{ - - if deviceOrientation != 2 { - enableShutterBtn(true) - deviceOrientation = 2 - hidenHorizontalScreenTips() - print("横屏"); - } - if x >= 0{//右横屏 - imageCGImagePropertyOrientation = .right - } - else{//左横屏 - imageCGImagePropertyOrientation = .left - } - } - - } - - - - //当屏幕转向发生改变时,自动结束录像 - func autoStopRecordVideo(){ - if self.isRecording { - stopRVideo() - } - } - - - - - deinit { - // 移除通知 -// // 结束生成设备旋转方向通知 -// UIDevice.current.endGeneratingDeviceOrientationNotifications() - print("shootcontroller deinit....") - if let timer = self.timer { - // 当视图控制器被释放时,取消定时器 - timer.invalidate() - } - } - - - - - - - - - //======= - - // private func startRecording() { - // guard let wideAngleCameraMovieOutput = wideAngleCameraMovieOutput, - // let ultraWideCameraMovieOutput = ultraWideCameraMovieOutput else { - // print("Movie output not configured") - // return - // } - // - // let time = Date().timeIntervalSince1970 - // let name1 = "back" + String(time) + ".mov" - // let name2 = "front" + String(time) + ".mov" - // let backCameraOutputURL = URL.documentsDirectory.appending(path:name1) - // let frontCameraOutputURL = URL.documentsDirectory.appending(path:name2) - // - // wideAngleCameraMovieOutput.startRecording(to: backCameraOutputURL, recordingDelegate: self) - // ultraWideCameraMovieOutput.startRecording(to: frontCameraOutputURL, recordingDelegate: self) - // - // isRecording = true - // startRecordingButton?.setTitle("Stop Recording", for: .normal) - // } - - // private func stopRecording() { - // guard let wideAngleCameraMovieOutput = wideAngleCameraMovieOutput, - // let ultraWideCameraMovieOutput = ultraWideCameraMovieOutput else { - // print("Movie output not configured") - // return - // } - // - // wideAngleCameraMovieOutput.stopRecording() - // ultraWideCameraMovieOutput.stopRecording() - // - // isRecording = false - // startRecordingButton?.setTitle("Start Recording", for: .normal) - // } - -} - - -extension CCSpatialShootController: AVCaptureFileOutputRecordingDelegate { - func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) { - if let error = error { - print("Video recording finished with error: \(error.localizedDescription)") - - DispatchQueue.main.async { - SVProgressHUD.showSuccess(withStatus: NSLocalizedString("合成过程出现问题", comment: "")) - } - - } else { - - DispatchQueue.main.async { - SVProgressHUD.show(withStatus: NSLocalizedString("处理中...", comment: "")) - print("....................................\n..................................") - } - - - if shootingMode == .CCShootingMode_Camera { - if output == wideAngleCameraMovieOutput { - print("wide Angle photo recorded: \(outputFileURL)") - if let firstFrame = self.firstFrame(from: outputFileURL) { - imgs.add(firstFrame) - } - else { - print("wide Angle firstframe is lost....") - } - } else if output == ultraWideCameraMovieOutput { - print("ultra Wide photo recorded: \(outputFileURL)") - if let firstFrame = self.firstFrame(from: outputFileURL) { - imgs.add(firstFrame) - } - else{ - print("ultra Wide firstFrame is lost....") - } - } - - if(imgs.count == 2){ - compositeSpatialPhoto() - } - else { - print("images 少于2张....") - } - - }else if shootingMode == .CCShootingMode_Video { - if output == wideAngleCameraMovieOutput { - print("wide Angle video recorded: \(outputFileURL)") - leftEyeVideoURL = outputFileURL - } else if output == ultraWideCameraMovieOutput { - print("ultra Wide video recorded: \(outputFileURL)") - rightEyeVideoURL = outputFileURL - } - if let leftEyeVideoURL, - let rightEyeVideoURL { - createSpVideo() - } - - } - } - } - - //MARK: 空间视频合成 - func createSpVideo(){ - - if(rightEyeVideoURL != nil && leftEyeVideoURL != nil){ - - Task { - spatialVideoWriter.writeSpatialVideo(leftEyeVideoURL: leftEyeVideoURL!, rightEyeVideoURL: rightEyeVideoURL!, outputVideoURL: outputVideoURL!) {[weak self] success, error in - DispatchQueue.main.async { - SVProgressHUD.dismiss() - print("SVProgressHUD.dismiss..2222.....") - } - if success { - print("空间视频生成成功") - if let ovrul = self?.outputVideoURL{ - self?.saveVideoToLibrary(videoURL:ovrul) - } - -// DispatchQueue.main.async { -// SVProgressHUD.showSuccess(withStatus: "空间视频成功保存到相册") -// } - } else if let error = error { - print(".怎么失败了呢.....error\(error)") - - DispatchQueue.main.async { - SVProgressHUD.showInfo(withStatus: "\(NSLocalizedString("空间视频保存失败", comment: "")):\(error.localizedDescription)") - } - } - else { - print("not success......") - } - } - } - } - } - - private func saveVideoToLibrary(videoURL: URL) { - PHPhotoLibrary.shared().performChanges({ - PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: videoURL) - }) {[weak self] success, error in - if success { - print("保存成功") - self?.getAlbumFirstPhoto() - } else if let error = error { - print("保存失败") - } - } - } - - - //MARK: 空间图片合成 - private func firstFrame(from videoURL: URL) -> UIImage? { - let asset = AVURLAsset(url: videoURL) - let generator = AVAssetImageGenerator(asset: asset) - generator.appliesPreferredTrackTransform = true - let time = CMTime(seconds: 0.0, preferredTimescale: 1) - do { - let cgImage = try generator.copyCGImage(at: time, actualTime: nil) - return UIImage(cgImage: cgImage) - } catch { - print("Error generating first frame: \(error.localizedDescription)") - return nil - } - } - - - //合成空间图片 - func compositeSpatialPhoto(){ - let img1:UIImage = imgs[0] as! UIImage - let img2:UIImage = imgs[1] as! UIImage - - let url = URL.documentsDirectory.appending(path:"aaa12.HEIC") - let destination = CGImageDestinationCreateWithURL(url as CFURL, UTType.heic.identifier as CFString, 2, nil)! - var oo = imageCGImagePropertyOrientation.rawValue - let orientation_cf = CFNumberCreate(nil, CFNumberType.intType, &oo) - let properties1 = [ - kCGImagePropertyGroups: [ - kCGImagePropertyGroupIndex: 0, - kCGImagePropertyGroupType: kCGImagePropertyGroupTypeStereoPair, - kCGImagePropertyGroupImageIndexLeft: 0, - kCGImagePropertyGroupImageIndexRight: 1, - ], - kCGImagePropertyTIFFDictionary:[ - kCGImagePropertyOrientation:orientation_cf, - ], - - kCGImagePropertyOrientation:orientation_cf as Any, - kCGImagePropertyHEIFDictionary: [ - kIIOMetadata_CameraExtrinsicsKey: [ - kIIOCameraExtrinsics_CoordinateSystemID: 0, - kIIOCameraExtrinsics_Position: [ - 0, - 0, - 0 - ], - kIIOCameraExtrinsics_Rotation: [ - 1, 0, 0, - 0, 1, 0, - 0, 0, 1 - ] - ] - ] - ] as [CFString : Any] - let properties2 = [ - kCGImagePropertyGroups: [ - kCGImagePropertyGroupIndex: 0, - kCGImagePropertyGroupType: kCGImagePropertyGroupTypeStereoPair, - kCGImagePropertyGroupImageIndexLeft: 0, - kCGImagePropertyGroupImageIndexRight: 1, - ], - kCGImagePropertyTIFFDictionary:[ - kCGImagePropertyOrientation:orientation_cf, - ], - kCGImagePropertyOrientation:orientation_cf as Any, - - kCGImagePropertyHEIFDictionary: [ - kIIOMetadata_CameraExtrinsicsKey: [ - kIIOCameraExtrinsics_CoordinateSystemID: 0, - kIIOCameraExtrinsics_Position: [ - -0.019238, - 0, - 0 - ], - kIIOCameraExtrinsics_Rotation: [ - 1, 0, 0, - 0, 1, 0, - 0, 0, 1 - ] - ] - ] - ] as [CFString : Any] - - let leftImg = img1//fixOrientation(img1) - let rightImg = img2//fixOrientation(img2) - - - - let p_dic1:CFDictionary = properties1 as CFDictionary - CGImageDestinationAddImage(destination, leftImg.cgImage!,p_dic1) - let p_dic2:CFDictionary = properties2 as CFDictionary - CGImageDestinationAddImage(destination, rightImg.cgImage!, p_dic2) - - let rr = CGImageDestinationFinalize(destination) - if rr == false { - print("ee..") - } - - - -// let source = CGImageSourceCreateWithURL(url as CFURL, nil)! -// guard let properties22 = CGImageSourceCopyPropertiesAtIndex(source, 1, nil) as? [CFString: Any] else { -// return -// } -// print("ssss:\(properties22)") - - savePhoto(url) - - } - - - - - //保存照片 - func savePhoto(_ fileURL: URL) { - - // 创建 PHAssetCreationRequest - PHPhotoLibrary.shared().performChanges({ - let creationRequest = PHAssetCreationRequest.forAsset() - creationRequest.addResource(with: .photoProxy, fileURL: fileURL, options: nil) - - }) {[weak self] success, error in - - DispatchQueue.main.async { - SVProgressHUD.dismiss() - print("SVProgressHUD.dismiss..1111.....") - } - - if let error = error { - print("Error saving photo to library: \(error.localizedDescription)") - DispatchQueue.main.async { - SVProgressHUD.showInfo(withStatus: "\(NSLocalizedString("空间图片保存失败", comment: "")): \(error.localizedDescription)") - } - } else { - print("Photo saved to library successfully.") - DispatchQueue.main.async { -// SVProgressHUD.showSuccess(withStatus: "空间图片成功保存到相册") - self?.getAlbumFirstPhoto() - } - } - } - } - -} diff --git a/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialShootController/CCSpatialShootController+CaputreAction.swift b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialShootController/CCSpatialShootController+CaputreAction.swift new file mode 100644 index 0000000..4366694 --- /dev/null +++ b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialShootController/CCSpatialShootController+CaputreAction.swift @@ -0,0 +1,286 @@ +// +// CCSpatialShootController+CaputreAction.swift +// SwiftProject +// +// Created by aaa on 2024/5/20. +// + +import Foundation + +import AVFoundation +import Photos +import AVKit +import VideoToolbox +import SVProgressHUD +import Firebase +import CoreMotion + +extension CCSpatialShootController { + + //拍照 + @objc func shutterPhotoButtonAction(){ + + + shutterPhotoButton.backgroundColor = .gray + + imgs.removeAllObjects() + guard let wideAngleCameraVideoDataOutput, + let ultraWideCameraVideoDataOutput else { + print("Movie output not configured") + return + } + + + //快门声音 + let soundID: SystemSoundID = 1108 + AudioServicesPlaySystemSound(soundID) + +// let time = Date().timeIntervalSince1970 +// let name1 = "wideAnglePhoto" + String(time) + ".mov" +// let name2 = "ultraWidePhoto" + String(time) + ".mov" +// let wideAngleCameraOutputURL = URL.documentsDirectory.appending(path:name1) +// let ultraWideCameraOutputURL = URL.documentsDirectory.appending(path:name2) +// +// wideAngleCameraMovieOutput.startRecording(to: wideAngleCameraOutputURL, recordingDelegate: self) +// ultraWideCameraMovieOutput.startRecording(to: ultraWideCameraOutputURL, recordingDelegate: self) + + //摄像头录像,取0.1秒长度,如果是拍摄照片去两张,拍摄成功会听到两次声音,所以这里用摄像的方式取图片 + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {[weak self] in + self?.shutterPhotoButton.backgroundColor = .white + self?.isTakePhoto_wideCamera = true + self?.isTakePhoto_ultraCamera = true + self?.isTakePhoto = true +// self.stopVideoRecording() + } + } + + //录像 + @objc func shutterVideoButtonAction(_ sender: UIButton){ + if !self.isRecording { + //录像中 + print("录像中...") + + UIView.animate(withDuration: 0.3) { + // 更新视图的transform属性,将其缩小 + self.shutterVideoButton.transform = CGAffineTransform(scaleX: 0.53, y: 0.53) + self.shutterVideoButton.layer.cornerRadius = 4 + }completion: { finished in + self.isRecording = true + self.changeSwitchstatus() + } + + + //开启录制 + startRecordingTimer() + startVideoRecording() + + + }else{ + stopRVideo() + } + } + + func stopRVideo(){ + print("录像结束!!!") + //停止录制 + stopRecordingTimer() + stopVideoRecording() + UIView.animate(withDuration: 0.3) { + // 更新视图的transform属性,将其放大 + self.shutterVideoButton.transform = CGAffineTransform(scaleX: 1.0, y: 1.0) + self.shutterVideoButton.layer.cornerRadius = 28 + }completion: { finished in + self.isRecording = false + self.changeSwitchstatus() + } + } + + // MARK: - 开始/停止录制 + private func startVideoRecording() { + guard let wideAngleCameraVideoDataOutput, + let ultraWideCameraVideoDataOutput else { + print("Movie output not configured") + return + } + +// let time = Date().timeIntervalSince1970 +// let name1 = "wideAngleVideo" + String(time) + ".mov" +// let name2 = "ultraWideVideo" + String(time) + ".mov" +// leftEyeVideoURL = nil +// rightEyeVideoURL = nil +// let wideAngleCameraOutputURL = URL.documentsDirectory.appending(path:name1) +// let fultraWideCameraOutputURL = URL.documentsDirectory.appending(path:name2) +// +// wideAngleCameraMovieOutput.startRecording(to: wideAngleCameraOutputURL, recordingDelegate: self) +// ultraWideCameraMovieOutput.startRecording(to: fultraWideCameraOutputURL, recordingDelegate: self) + + + } + + private func stopVideoRecording(){ +// guard let wideAngleCameraMovieOutput = wideAngleCameraMovieOutput, +// let ultraWideCameraMovieOutput = ultraWideCameraMovieOutput else { +// print("Movie output not configured") +// return +// } +// +// wideAngleCameraMovieOutput.stopRecording() +// ultraWideCameraMovieOutput.stopRecording() + + } + + //MARK: - 改变按钮状态 + func changeSwitchstatus() { + + if isRecording { + self.switchBackView.isHidden = true + self.vipLogoView.isHidden = true + cameraLabel.isHidden = true + videoLabel.isHidden = true + }else{ + self.switchBackView.isHidden = false + if !UserInfo.sharedInstance.isMemberShip { + self.vipLogoView.isHidden = false + } + + if shootingMode == .CCShootingMode_Camera { + //拍照 + cameraLabel.isHidden = false + videoLabel.isHidden = true + + cameraButton.backgroundColor = UIColor(hexString: "#000000", alpha: 0.3) + cameraButton.setImage(UIImage(named: "camera_button_selected"), for: .normal) + + videoButton.backgroundColor = .clear + videoButton.setImage(UIImage(named: "video_button_normal"), for: .normal) + + }else if shootingMode == .CCShootingMode_Video { + //录像 + cameraLabel.isHidden = true + videoLabel.isHidden = false + + cameraButton.backgroundColor = .clear + cameraButton.setImage(UIImage(named: "camera_button_normal"), for: .normal) + + videoButton.backgroundColor = UIColor(hexString: "#000000", alpha: 0.3) + videoButton.setImage(UIImage(named: "video_button_selected"), for: .normal) + } + } + } + + + // MARK: - 开启/关闭定时器 + func startRecordingTimer() { + var count = 0 // 初始化计数变量为0 + timer = Timer(timeInterval: 1, repeats: true, block: { timer in + DispatchQueue.global().async { + // 在后台线程中执行任务 + // 模拟耗时操作(这里只是打印当前计数) + count += 1 + let timeText = self.transToHourMinSec(second: count) + DispatchQueue.main.async { +// self.setNavgaionTitle(string: timeText) + self.setNavgationTitleForCaptureVC(string: timeText) + } + } + }) + + //录制过程中timer会被暂停,只有加入到runloop中才能执行 + RunLoop.main.add(timer!, forMode: .common) + } + + func stopRecordingTimer() { + self.setNavgaionTitle(string: "") + if let timer = self.timer { + // 当视图控制器被释放时,取消定时器 + timer.invalidate() + } + } + + + + // MARK: - 把秒数转换成时分秒(00:00:00)格式 + /// + /// - Parameter second: 秒数 + /// - Returns: String + func transToHourMinSec(second: Int) -> String { + let allTime: Int = second + var hours = 0 + var minutes = 0 + var seconds = 0 + var hoursText = "" + var minutesText = "" + var secondsText = "" + + hours = allTime / 3600 + hoursText = hours > 9 ? "\(hours)" : "0\(hours)" + + minutes = allTime % 3600 / 60 + minutesText = minutes > 9 ? "\(minutes)" : "0\(minutes)" + + seconds = allTime % 3600 % 60 + secondsText = seconds > 9 ? "\(seconds)" : "0\(seconds)" + + return "\(hoursText):\(minutesText):\(secondsText)" + } + + + //MARK: - 获取相册第一张图片 + func getAlbumFirstPhoto() { + PHPhotoLibrary.requestAuthorization(for: .readWrite) {[weak self] (status) in + if status == PHAuthorizationStatus.authorized { + let fetchOptions = PHFetchOptions() + fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: false)] + + let image_assetsFetchResults: PHFetchResult! = PHAsset.fetchAssets(with: .image, options: fetchOptions) + + let video_assetsFetchResults: PHFetchResult! = PHAsset.fetchAssets(with: .video, options: fetchOptions) + + let imgAsset = image_assetsFetchResults.firstObject + let videoAsset = video_assetsFetchResults.firstObject + var defaultAsset:PHAsset? + if let imgAsset { + defaultAsset = imgAsset + } + + if let videoAsset{ + if let temp = defaultAsset { + if (videoAsset.creationDate ?? Date.now > imgAsset?.creationDate ?? Date.now) { + defaultAsset = videoAsset + } + } + else { + defaultAsset = videoAsset + } + } + + if let defaultAsset { + self?.getImageFromAsset(asset: defaultAsset) + } + } + } + } + + func getImageFromAsset(asset:PHAsset){ + //处理模型 + let requestOptions = PHImageRequestOptions() + requestOptions.isSynchronous = false//设置成同步回调 + requestOptions.deliveryMode = .highQualityFormat + PHImageManager.default().requestImageDataAndOrientation(for: asset, options: requestOptions) {[weak self] data, dataUTI, imagePropertyOrientation, info in + if let hasData = data { + DispatchQueue.main.async { + self?.albumButton.setBackgroundImage(UIImage(data: hasData), for: .normal) + } + } + + } + } + + + func enableShutterBtn(_ value:Bool) { + shutterPhotoButton.isEnabled = value + shutterVideoButton.isEnabled = value + } + + +} diff --git a/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialShootController/CCSpatialShootController+Generate.swift b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialShootController/CCSpatialShootController+Generate.swift new file mode 100644 index 0000000..66f18a5 --- /dev/null +++ b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialShootController/CCSpatialShootController+Generate.swift @@ -0,0 +1,295 @@ +// +// CCSpatialShootController+Generate.swift +// SwiftProject +// +// Created by aaa on 2024/5/20. +// + +import Foundation +import AVFoundation +import Photos +import AVKit +import VideoToolbox +import SVProgressHUD +import Firebase +import CoreMotion + +extension CCSpatialShootController: AVCaptureAudioDataOutputSampleBufferDelegate,AVCaptureVideoDataOutputSampleBufferDelegate { + + //视频、音频回调 + func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { + if shootingMode == .CCShootingMode_Camera && self.isTakePhoto{ + takePhotoSemaphore.wait() + print("xxsddd:\(Thread.current)") + if output == wideAngleCameraVideoDataOutput && self.isTakePhoto_wideCamera { + if let firstFrame = imageFromSampleBuffer(sampleBuffer: sampleBuffer) { + imgs.add(firstFrame) + self.isTakePhoto_wideCamera = false + } + else { + print("wide Angle firstframe is lost....") + } + } else if output == ultraWideCameraVideoDataOutput && self.isTakePhoto_ultraCamera{ + if let firstFrame = imageFromSampleBuffer(sampleBuffer: sampleBuffer) { + imgs.add(firstFrame) + self.isTakePhoto_ultraCamera = false + } + else{ + print("ultra Wide firstFrame is lost....") + } + } + + if(imgs.count == 2){ + compositeSpatialPhoto() + imgs.removeAllObjects() + self.isTakePhoto = false + print("2张照片满足合成....") + } + else { + print("images 少于2张....") + } + takePhotoSemaphore.signal() + + } + + else if shootingMode == .CCShootingMode_Video { + if output == wideAngleCameraVideoDataOutput {//广角视频 +// leftEyeVideoURL = outputFileURL + } + else if output == ultraWideCameraVideoDataOutput {//超广角视频 +// print("ultra Wide video recorded: \(outputFileURL)") +// rightEyeVideoURL = outputFileURL + } + else if output == wuCameraAudioDataOutput {//音频 + + } + +// if let leftEyeVideoURL, +// let rightEyeVideoURL { +// createSpVideo() +// } + + } + } + + //视频掉帧回调 + func captureOutput(_ output: AVCaptureOutput, didDrop sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { + + } + + + + //通过samplebuffer生成uiimage + func imageFromSampleBuffer(sampleBuffer: CMSampleBuffer) -> UIImage? { + // 获取像素缓冲区 + guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return nil } + + // 创建CoreImage图像 + let ciImage = CIImage(cvImageBuffer: imageBuffer) + + // 将CIImage转换为CGImage + if let cgImage = CIContext(options: nil).createCGImage(ciImage, from: ciImage.extent) { + // 创建UIImage + let image = UIImage(cgImage: cgImage) + return image + } + + return nil + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + //MARK: 空间视频合成 + func createSpVideo(){ +// +// if(rightEyeVideoURL != nil && leftEyeVideoURL != nil){ +// +// Task { +// spatialVideoWriter.writeSpatialVideo(leftEyeVideoURL: leftEyeVideoURL!, rightEyeVideoURL: rightEyeVideoURL!, outputVideoURL: outputVideoURL!) {[weak self] success, error in +// DispatchQueue.main.async { +// SVProgressHUD.dismiss() +// print("SVProgressHUD.dismiss..2222.....") +// } +// if success { +// print("空间视频生成成功") +// if let ovrul = self?.outputVideoURL{ +// self?.saveVideoToLibrary(videoURL:ovrul) +// } +// +// } else if let error = error { +// print(".怎么失败了呢.....error\(error)") +// +// DispatchQueue.main.async { +// SVProgressHUD.showInfo(withStatus: "\(NSLocalizedString("空间视频保存失败", comment: "")):\(error.localizedDescription)") +// } +// } +// else { +// print("not success......") +// } +// } +// } +// } + } + + private func saveVideoToLibrary(videoURL: URL) { + PHPhotoLibrary.shared().performChanges({ + PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: videoURL) + }) {[weak self] success, error in + if success { + print("保存成功") + self?.getAlbumFirstPhoto() + } else if let error = error { + print("保存失败") + } + } + } + + + + + //合成空间图片 + func compositeSpatialPhoto(){ + let img1:UIImage = imgs[0] as! UIImage + let img2:UIImage = imgs[1] as! UIImage + + let url = URL.documentsDirectory.appending(path:"aaa12.HEIC") + let destination = CGImageDestinationCreateWithURL(url as CFURL, UTType.heic.identifier as CFString, 2, nil)! + var oo = imageCGImagePropertyOrientation.rawValue + let orientation_cf = CFNumberCreate(nil, CFNumberType.intType, &oo) + let properties1 = [ + kCGImagePropertyGroups: [ + kCGImagePropertyGroupIndex: 0, + kCGImagePropertyGroupType: kCGImagePropertyGroupTypeStereoPair, + kCGImagePropertyGroupImageIndexLeft: 0, + kCGImagePropertyGroupImageIndexRight: 1, + ], + kCGImagePropertyTIFFDictionary:[ + kCGImagePropertyOrientation:orientation_cf, + ], + + kCGImagePropertyOrientation:orientation_cf as Any, + kCGImagePropertyHEIFDictionary: [ + kIIOMetadata_CameraExtrinsicsKey: [ + kIIOCameraExtrinsics_CoordinateSystemID: 0, + kIIOCameraExtrinsics_Position: [ + 0, + 0, + 0 + ], + kIIOCameraExtrinsics_Rotation: [ + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + ] + ] + ] + ] as [CFString : Any] + let properties2 = [ + kCGImagePropertyGroups: [ + kCGImagePropertyGroupIndex: 0, + kCGImagePropertyGroupType: kCGImagePropertyGroupTypeStereoPair, + kCGImagePropertyGroupImageIndexLeft: 0, + kCGImagePropertyGroupImageIndexRight: 1, + ], + kCGImagePropertyTIFFDictionary:[ + kCGImagePropertyOrientation:orientation_cf, + ], + kCGImagePropertyOrientation:orientation_cf as Any, + + kCGImagePropertyHEIFDictionary: [ + kIIOMetadata_CameraExtrinsicsKey: [ + kIIOCameraExtrinsics_CoordinateSystemID: 0, + kIIOCameraExtrinsics_Position: [ + -0.019238, + 0, + 0 + ], + kIIOCameraExtrinsics_Rotation: [ + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + ] + ] + ] + ] as [CFString : Any] + + let leftImg = img1//fixOrientation(img1) + let rightImg = img2//fixOrientation(img2) + + + + let p_dic1:CFDictionary = properties1 as CFDictionary + CGImageDestinationAddImage(destination, leftImg.cgImage!,p_dic1) + let p_dic2:CFDictionary = properties2 as CFDictionary + CGImageDestinationAddImage(destination, rightImg.cgImage!, p_dic2) + + let rr = CGImageDestinationFinalize(destination) + if rr == false { + print("ee..") + } + savePhoto(url) + + } + + + + + //保存照片 + func savePhoto(_ fileURL: URL) { + + // 创建 PHAssetCreationRequest + PHPhotoLibrary.shared().performChanges({ + let creationRequest = PHAssetCreationRequest.forAsset() + creationRequest.addResource(with: .photoProxy, fileURL: fileURL, options: nil) + + }) {[weak self] success, error in + + DispatchQueue.main.async { + SVProgressHUD.dismiss() + print("SVProgressHUD.dismiss..1111.....") + } + + if let error = error { + print("Error saving photo to library: \(error.localizedDescription)") + DispatchQueue.main.async { + SVProgressHUD.showInfo(withStatus: "\(NSLocalizedString("空间图片保存失败", comment: "")): \(error.localizedDescription)") + } + } else { + print("Photo saved to library successfully.") + DispatchQueue.main.async { + self?.getAlbumFirstPhoto() + } + } + } + } + +} diff --git a/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialShootController/CCSpatialShootController+SessionConfigure.swift b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialShootController/CCSpatialShootController+SessionConfigure.swift new file mode 100644 index 0000000..2f09f93 --- /dev/null +++ b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialShootController/CCSpatialShootController+SessionConfigure.swift @@ -0,0 +1,162 @@ +// +// CCSpatialShootController+SessionConfigure.swift +// SwiftProject +// +// Created by aaa on 2024/5/20. +// + +import Foundation + +import AVFoundation +import Photos +import AVKit +import VideoToolbox +import SVProgressHUD +import Firebase +import CoreMotion + +extension CCSpatialShootController { + + func configureSession() { + session.beginConfiguration() + defer { + session.commitConfiguration() + } + + // 配置后置摄像头 .builtInWideAngleCamera 广角摄像头(默认设备 --- 主摄,28mm左右焦段) .back 后置摄像头 + guard let backCamera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else { + print("Could not find the back camera") + return + } + wideAngleCameraDevice = backCamera + + do { + wideAngleCameraDeviceInput = try AVCaptureDeviceInput(device: backCamera) + + guard let wideAngleCameraDeviceInput = wideAngleCameraDeviceInput, + session.canAddInput(wideAngleCameraDeviceInput) else { + print("Could not add back camera input") + return + } + session.addInput(wideAngleCameraDeviceInput) + } catch { + print("Could not create back camera device input: \(error)") + return + } + + // .builtInUltraWideCamera 超广角(默认设备的0.5x,只能使用AVCaptureDeviceDiscoverySession获取) .back 后置摄像头 + guard let frontCamera = AVCaptureDevice.default(.builtInUltraWideCamera, for: .video, position: .back) else { + print("Could not find the front camera") + return + } + + do { + print("builtInUltraWideCamera:\(frontCamera.videoZoomFactor)") + + // 设置焦距 +// frontCamera.focusMode = .autoFocus + ultraWideCameraDeviceInput = try AVCaptureDeviceInput(device: frontCamera) + guard let ultraWideCameraDeviceInput = ultraWideCameraDeviceInput, + session.canAddInput(ultraWideCameraDeviceInput) else { + print("Could not add front camera input") + return + } + session.addInput(ultraWideCameraDeviceInput) + } catch { + print("Could not create front camera device input: \(error)") + return + } + + // 配置音频输入设备 + guard let audioDevice = AVCaptureDevice.default(for: .audio) else { + print("Could not find audio device") + return + } + + do { + let audioDeviceInput = try AVCaptureDeviceInput(device: audioDevice) + + guard session.canAddInput(audioDeviceInput) else { + print("Could not add audio input") + return + } + + session.addInput(audioDeviceInput) + } catch { + print("Could not create audio device input: \(error)") + return + } + + // 配置广角摄像头输出 +// wideAngleCameraMovieOutput = AVCaptureMovieFileOutput() +// guard let wideAngleCameraMovieOutput = wideAngleCameraMovieOutput, +// session.canAddOutput(wideAngleCameraMovieOutput) else { +// print("Could not add the back camera movie output") +// return +// } +// session.addOutput(wideAngleCameraMovieOutput) + + wideAngleCameraVideoDataOutput = AVCaptureVideoDataOutput() + guard let wideAngleCameraVideoDataOutput, session.canAddOutput(wideAngleCameraVideoDataOutput) else { + print("wideAngleCameraVideoDataOutput....") + return + } + wideAngleCameraVideoDataOutput.setSampleBufferDelegate(self, queue: DispatchQueue.global()) +// wideAngleCameraVideoDataOutput.videoSettings = +// [NSDictionary dictionaryWithObject: +// [NSNumber numberWithInt:kCVPixelFormatType_32BGRA] +// forKey:(id)kCVPixelBufferPixelFormatTypeKey]; +// wideAngleCameraVideoDataOutput.minFrameDuration = CMTimeMake(1, 15); + session.addOutput(wideAngleCameraVideoDataOutput) + + // 配置超广角摄像头输出 +// ultraWideCameraMovieOutput = AVCaptureMovieFileOutput() +// guard let ultraWideCameraMovieOutput = ultraWideCameraMovieOutput, +// session.canAddOutput(ultraWideCameraMovieOutput) else { +// print("Could not add the front camera movie output") +// return +// } +// session.addOutput(ultraWideCameraMovieOutput) + + ultraWideCameraVideoDataOutput = AVCaptureVideoDataOutput() + guard let ultraWideCameraVideoDataOutput, + session.canAddOutput(ultraWideCameraVideoDataOutput) else { + print("ultraWideCameraVideoDataOutput....") + return + } + ultraWideCameraVideoDataOutput.setSampleBufferDelegate(self, queue: DispatchQueue.global()) + session.addOutput(ultraWideCameraVideoDataOutput) + + //配置声音输出 + wuCameraAudioDataOutput = AVCaptureAudioDataOutput() + guard let wuCameraAudioDataOutput, + session.canAddOutput(wuCameraAudioDataOutput) else { + print("wuCameraAudioDataOutput....") + return + } + wuCameraAudioDataOutput.setSampleBufferDelegate(self, queue: DispatchQueue.global()) + session.addOutput(wuCameraAudioDataOutput) + + // + + // 配置预览图层 --- 主摄 + wideAngleCameraVideoPreviewLayer = AVCaptureVideoPreviewLayer(session: session) + wideAngleCameraVideoPreviewLayer?.frame = CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height) + if let wideAngleCameraVideoPreviewLayer = wideAngleCameraVideoPreviewLayer { + wideAngleCameraVideoPreviewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill + self.view.layer.insertSublayer(wideAngleCameraVideoPreviewLayer, at: 0) + } + + + DispatchQueue.global().async { + self.session.startRunning() + + //变焦设置需要在AVCaptureMultiCamSession statrunning之后设置才会生效 + try! frontCamera.lockForConfiguration() + frontCamera.videoZoomFactor = 1.75//变焦 + frontCamera.unlockForConfiguration() + + } + } + +} diff --git a/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialShootController/CCSpatialShootController.swift b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialShootController/CCSpatialShootController.swift new file mode 100644 index 0000000..dd3c3e3 --- /dev/null +++ b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialShootController/CCSpatialShootController.swift @@ -0,0 +1,554 @@ +// +// CCSpatialShootController.swift +// SwiftProject +// +// Created by Zhang, Joyce on 2024/2/29. +// + +import UIKit +import AVFoundation +import Photos +import AVKit +import VideoToolbox +import SVProgressHUD +import Firebase +import CoreMotion +class CCSpatialShootController: BaseController { + + + let kNowTimeToUserDefaultKey_SpatialShootController:String = "kNowTimeToUserDefaultKey_SpatialShootController" + var wideAngleCameraDevice:AVCaptureDevice?//测试使用 + var isTakePhoto = false//是否点击拍照按钮 + var isTakePhoto_ultraCamera = false + var isTakePhoto_wideCamera = false + let takePhotoSemaphore = DispatchSemaphore(value: 1) + + //AVCaptureSession --- 单摄像头 + var session = AVCaptureMultiCamSession()//多摄像头 + var wideAngleCameraDeviceInput: AVCaptureDeviceInput?//广角摄像头 .builtInWideAngleCamera + var ultraWideCameraDeviceInput: AVCaptureDeviceInput?//超广角 .builtInUltraWideCamera + + + var wideAngleCameraVideoPreviewLayer: AVCaptureVideoPreviewLayer?//广角摄像头 + var ultraWideCameraVideoPreviewLayer: AVCaptureVideoPreviewLayer?//超广角 + +// var wideAngleCameraMovieOutput: AVCaptureMovieFileOutput? +// var ultraWideCameraMovieOutput: AVCaptureMovieFileOutput? + + + var wideAngleCameraVideoDataOutput:AVCaptureVideoDataOutput? + var ultraWideCameraVideoDataOutput:AVCaptureVideoDataOutput? + var wuCameraAudioDataOutput:AVCaptureAudioDataOutput? + + + var isRecording = false + var imageCGImagePropertyOrientation:CGImagePropertyOrientation = CGImagePropertyOrientation.left//由于cgimage无法保留图像的方向 + var deviceOrientation = 1//监听设备方向 1,表示竖屏;2,表示横屏 + + var leftEyeVideoURL:URL? + var rightEyeVideoURL:URL? + var outputVideoURL: URL? + let motionManager = CMMotionManager() + var imgs:NSMutableArray = NSMutableArray() //存放广角、主摄照片数组 + let spatialVideoWriter = SpatialVideoWriter() + //================================ + + //当前相机模式 + var shootingMode:CCShootingMode = .CCShootingMode_Camera + + //记录拍摄时长 + var timer: Timer? + + lazy var spaceAlbumPopView: CCSpaceAlbumFilterPopView2 = { + let view = CCSpaceAlbumFilterPopView2.init(frame: CGRectMake(0, 0, KScreenWidth, KScreenHeight)) + return view + }() + + lazy var tipsLabel: UILabel = { + let label = UILabel.init(frame: CGRectMake((KScreenWidth - 320)/2, KNavigationBarHeight+KStatusBarHeight + 20, 320, 36)) + label.backgroundColor = UIColor.darkGray + label.font = KFont_Medium(12) + label.textColor = KTextColor_White + label.text = ""//你只需捕捉美好,转码交给我们" + label.textAlignment = .center + label.alpha = 1.0 + label.layer.cornerRadius = 8 + label.clipsToBounds = true + label.isHidden = true + return label + }() + + lazy var horizontalImageView: UIImageView = { + let imageView = UIImageView() + imageView.image = UIImage.init(named: "horizontal_rotate_img") + imageView.alpha = 0.0 + imageView.isHidden = true + return imageView + }() + + lazy var effectView: UIVisualEffectView = { + let effect = UIBlurEffect(style: .light) + let view = UIVisualEffectView.init(effect: effect) + view.backgroundColor = .clear + view.alpha = 0.0 + view.isHidden = true + return view + }() + + + lazy var horizontalLabel: UILabel = { + let label = UILabel() + label.backgroundColor = UIColor.clear + label.font = KFont_Medium(14) + label.textColor = KTextColor_White + label.text = NSLocalizedString("将iPhone旋转为横向", comment: "") + label.alpha = 0.0 + label.isHidden = true + return label + }() + + lazy var shutterRingView: UIView = { + let view = UIView() + view.frame = CGRect(x:0,y:0,width:66,height:66) + view.backgroundColor = .clear + view.layer.cornerRadius = 33 + view.layer.masksToBounds = true + view.layer.borderWidth = 3 + view.layer.borderColor = UIColor.white.cgColor + view.center = CGPointMake(KScreenWidth/2, KScreenHeight - KTabbarSafeBottomMargin - 50 - 33) + return view + }() + + lazy var shutterPhotoButton: UIButton = { + let botton = UIButton() + botton.frame = CGRect(x:0,y:0,width:56,height:56) + botton.backgroundColor = .white + botton.layer.cornerRadius = 28 + botton.layer.masksToBounds = true + botton.center = CGPointMake(33, 33) + botton.addTarget(self, action: #selector(shutterPhotoButtonAction), for: .touchUpInside) + return botton + }() + + lazy var shutterVideoButton: UIButton = { + let botton = UIButton() + botton.frame = CGRect(x:0,y:0,width:56,height:56) + botton.backgroundColor = UIColor(hexString: "#FF3B2F") + botton.layer.cornerRadius = 28 + botton.layer.masksToBounds = true + botton.center = CGPointMake(33, 33) + botton.addTarget(self, action: #selector(shutterVideoButtonAction(_:)), for: .touchUpInside) + botton.isHidden = true + return botton + }() + + + + + lazy var albumButton: UIButton = { + let botton = UIButton() + botton.frame = CGRect(x:0,y:0,width:48,height:48) + botton.layer.cornerRadius = 8 + botton.layer.masksToBounds = true + botton.center = CGPointMake(shutterRingView.center.x - 92 - 24, shutterRingView.center.y) + botton.addTarget(self, action: #selector(albumButtonAction), for: .touchUpInside) + botton.contentMode = .scaleAspectFill + return botton + }() + + + lazy var lightButton: UIButton = { + let botton = UIButton() + botton.frame = CGRect(x:0,y:0,width:40,height:40) + botton.setImage(UIImage.init(named: "light_button_normal"), for: .normal) + botton.center = CGPointMake( shutterRingView.center.x + 103 + 20, shutterRingView.center.y) + botton.addTarget(self, action: #selector(lightButtonAction), for: .touchUpInside) + return botton + }() + + + lazy var switchBackView: UIView = { + let view = UIView() + view.backgroundColor = UIColor(hexString: "#ffffff", alpha: 0.6) + view.layer.cornerRadius = 20 + view.layer.masksToBounds = true + return view + }() + + lazy var vipLogoView:UIImageView = { + let imgView = UIImageView() + imgView.image = UIImage(named: "camera_vip") + return imgView + }() + + lazy var cameraButton: UIButton = { + let botton = UIButton() + botton.tag = 1001 + botton.backgroundColor = UIColor(hexString: "#000000", alpha: 0.3) + botton.setImage(UIImage(named: "camera_button_selected"), for: .normal) + botton.layer.cornerRadius = 16 + botton.layer.masksToBounds = true + botton.addTarget(self, action: #selector(switchButtonAction(_:)), for: .touchUpInside) + return botton + }() + + lazy var videoButton: UIButton = { + let botton = UIButton() + botton.tag = 1002 + botton.backgroundColor = .clear + botton.setImage(UIImage(named: "video_button_normal"), for: .normal) + botton.layer.cornerRadius = 16 + botton.layer.masksToBounds = true + botton.addTarget(self, action: #selector(switchButtonAction(_:)), for: .touchUpInside) + return botton + }() + + lazy var cameraLabel: UILabel = { + let label = UILabel() + label.backgroundColor = UIColor.clear + label.font = KFont_Medium(12) + label.textColor = UIColor(hexString: "#ffffff", alpha: 0.6) + label.text = NSLocalizedString("拍照", comment: "") + label.isHidden = false + return label + }() + + lazy var videoLabel: UILabel = { + let label = UILabel() + label.backgroundColor = UIColor.clear + label.font = KFont_Medium(12) + label.textColor = UIColor(hexString: "#ffffff", alpha: 0.6) + label.text = NSLocalizedString("摄像", comment: "") + label.isHidden = true + return label + }() + + //================================= + //MARK: - viewWillAppear + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + + self.setNavgationBarColor(color: UIColor.clear) + self.setNavgationBarLine(color: .clear) + } + + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + let sec:TimeInterval = ZZHHelper.getSecFromUserDefaultByKey(kNowTimeToUserDefaultKey_SpatialShootController) + Analytics.logEvent("capture_pv", parameters: ["refer_page":"相机拍摄页面","duration":sec]) + } + + override func viewDidLoad() { + super.viewDidLoad() + ZZHHelper.setNowTimeToUserDefaultWithKey(kNowTimeToUserDefaultKey_SpatialShootController) + + outputVideoURL = URL.documentsDirectory.appendingPathComponent("output.MOV") + configureSession() // 设置相机参数 + + setUI() + self.view.bringSubviewToFront(navtionImgView!) + + //判断当前是否是横屏 + let isLandscape = UIDevice.current.orientation.isLandscape + if !isLandscape { + showHorizontalScreenTips() + } + + if motionManager.isDeviceMotionAvailable { + motionManager.startDeviceMotionUpdates(to: OperationQueue.main) {[weak self] motion, error in + if let motion = motion { + + // 处理设备方向变化 + self?.dealDeviceScreenOrientationByMotaion(motion) + } + } + } + + + + //读取相册第一张照片 + getAlbumFirstPhoto() + enableShutterBtn(false) + //进入页面的首选项调整为视频拍摄 + switchButtonAction(videoButton) + } + + private func setUI() { + + self.view.addSubview(effectView) + self.view.addSubview(horizontalImageView) + self.view.addSubview(horizontalLabel) + self.view.bringSubviewToFront(horizontalImageView) + self.view.bringSubviewToFront(horizontalLabel) + + //快门按钮 + self.view.addSubview(shutterRingView) + + shutterRingView.addSubview(shutterPhotoButton) + shutterRingView.addSubview(shutterVideoButton) + + self.view.addSubview(albumButton) + self.view.addSubview(lightButton) + + self.view.addSubview(switchBackView) + switchBackView.addSubview(cameraButton) + + switchBackView.addSubview(videoButton) + if !UserInfo.sharedInstance.isMemberShip { + self.view.addSubview(vipLogoView) + } + + + self.view.addSubview(cameraLabel) + self.view.addSubview(videoLabel) + self.setRightOneBtnTitle(string: "4K·3D") + + effectView.snp.makeConstraints { (make) in + make.top.leading.bottom.trailing.equalTo(self.view) + } + + horizontalImageView.snp.makeConstraints { (make) in + make.centerY.equalTo(self.view.snp.centerY) + make.centerX.equalTo(self.view.snp.centerX) + make.width.equalTo(28) + make.height.equalTo(28) + } + + horizontalLabel.snp.makeConstraints { (make) in + make.top.equalTo(self.horizontalImageView.snp.bottom).offset(16) + make.centerX.equalTo(self.view.snp.centerX) + } + + switchBackView.snp.makeConstraints { (make) in + make.bottom.equalTo(shutterRingView.snp.top).offset(-32) + make.centerX.equalTo(self.view.snp.centerX) + make.width.equalTo(128) + make.height.equalTo(40) + } + if !UserInfo.sharedInstance.isMemberShip { + vipLogoView.snp.makeConstraints { make in + make.width.equalTo(25) + make.height.equalTo(12) + make.left.equalTo(switchBackView.snp.right).offset(-22) + make.bottom.equalTo(switchBackView.snp.top).offset(12) + } + } + + + + cameraButton.snp.makeConstraints { (make) in + make.centerY.equalTo(switchBackView.snp.centerY) + + make.trailing.equalTo(switchBackView.snp.trailing).offset(-4) + make.width.equalTo(56) + make.height.equalTo(32) + } + + + + videoButton.snp.makeConstraints { (make) in + make.centerY.equalTo(switchBackView.snp.centerY) + make.leading.equalTo(switchBackView.snp.leading).offset(4) + make.width.equalTo(56) + make.height.equalTo(32) + } + + cameraLabel.snp.makeConstraints { (make) in + make.bottom.equalTo(switchBackView.snp.top).offset(-8) + + make.centerX.equalTo(switchBackView.snp.trailing).offset(-32) + } + + videoLabel.snp.makeConstraints { (make) in + make.bottom.equalTo(switchBackView.snp.top).offset(-8) + make.centerX.equalTo(switchBackView.snp.leading).offset(32) + } + } + + + + + + func showHorizontalScreenTips() { + self.horizontalImageView.isHidden = false + self.horizontalLabel.isHidden = false + + //模糊遮罩 + self.effectView.isHidden = false + + UIView.animate(withDuration: 0.5) { + self.horizontalImageView.alpha = 1.0 + self.horizontalLabel.alpha = 1.0 + //模糊遮罩 + self.effectView.alpha = 1.0 + + } + + } + + func hidenHorizontalScreenTips() { + UIView.animate(withDuration: 0.5) { + self.horizontalImageView.alpha = 0.0 + self.horizontalLabel.alpha = 0.0 + //模糊遮罩 + self.effectView.alpha = 0.0 + + }completion: { finished in + self.horizontalImageView.isHidden = true + self.horizontalLabel.isHidden = true + //模糊遮罩 + self.effectView.isHidden = true + + } + + } + + + //MARK: - action + @objc func albumButtonAction() { + // let vc:CCSpaceAlbumController = CCSpaceAlbumController() + // self.present(vc, animated: true, completion: nil) + self.view.addSubview(spaceAlbumPopView) + spaceAlbumPopView.show() + + //选择图片 + spaceAlbumPopView.selectedImageHandler = { [self] data,asset in + print("回调") + + let vc = CCSpatialPhotoDisplayController() + vc.photoOriginalData = data + vc.imageAsset = asset + + self.navigationController?.pushViewController(vc, animated: true) + } + //选择视频 + spaceAlbumPopView.selectedVideoHandler = { url,asset in + if (asset.duration > 60 ) { + if !UserInfo.sharedInstance.isMemberShip { + let vc:MembershipVC = MembershipVC() + let nav = UINavigationController(rootViewController: vc) + nav.modalTransitionStyle = UIModalTransitionStyle.coverVertical + nav.modalPresentationStyle = .fullScreen + self.present(nav, animated: true) + return + } + } + let vc = CCSpatialVideoDisplayController() + vc.sourceVideoURL = url + vc.videoOriginalPHAsset = asset + self.navigationController?.pushViewController(vc, animated: true) + } + + } + + + + + @objc func lightButtonAction() { + + guard let device = AVCaptureDevice.default(for: .video) else { + print("无法获取到您的设备") + return + } + if device.hasTorch && device.isTorchAvailable{ + try? device.lockForConfiguration() + + device.torchMode = device.torchMode == .off ? .on : .off + + device.unlockForConfiguration() + } + } + + @objc func switchButtonAction(_ sender: UIButton){ + if sender.tag == 1001 { + if UserInfo.sharedInstance.isMemberShip { + //拍照 + shootingMode = .CCShootingMode_Camera + + shutterPhotoButton.isHidden = false + shutterVideoButton.isHidden = true + changeSwitchstatus() + } + else {//前往内购订阅页面 + let vc:MembershipVC = MembershipVC() + let nav = UINavigationController(rootViewController: vc) + nav.modalTransitionStyle = UIModalTransitionStyle.coverVertical + nav.modalPresentationStyle = .fullScreen + self.present(nav, animated: true) + //统计 + Analytics.logEvent("capture_unvip_pv", parameters: nil) + + } + + }else if sender.tag == 1002 { + //录像 + shootingMode = .CCShootingMode_Video + + shutterPhotoButton.isHidden = true + shutterVideoButton.isHidden = false + changeSwitchstatus() + } + + + } + + + //MARK: - 屏幕旋转监听 + func dealDeviceScreenOrientationByMotaion(_ motion:CMDeviceMotion){ + + let x = motion.gravity.x + let y = motion.gravity.y + + if (fabs(y) >= fabs(x)){ + + if deviceOrientation != 1 { + enableShutterBtn(false) + deviceOrientation = 1 + print("竖屏"); + autoStopRecordVideo() + showHorizontalScreenTips() + } + + } + else{ + + if deviceOrientation != 2 { + enableShutterBtn(true) + deviceOrientation = 2 + hidenHorizontalScreenTips() + print("横屏"); + } + if x >= 0{//右横屏 + imageCGImagePropertyOrientation = .right + } + else{//左横屏 + imageCGImagePropertyOrientation = .left + } + } + + } + + + + //当屏幕转向发生改变时,自动结束录像 + func autoStopRecordVideo(){ + if self.isRecording { + stopRVideo() + } + } + + + + + deinit { + // 移除通知 +// // 结束生成设备旋转方向通知 + print("shootcontroller deinit....") + if let timer = self.timer { + // 当视图控制器被释放时,取消定时器 + timer.invalidate() + } + } +}