diff --git a/SwiftProject/SwiftProject.xcodeproj/project.pbxproj b/SwiftProject/SwiftProject.xcodeproj/project.pbxproj index eaaebdf..5146d59 100644 --- a/SwiftProject/SwiftProject.xcodeproj/project.pbxproj +++ b/SwiftProject/SwiftProject.xcodeproj/project.pbxproj @@ -23,6 +23,10 @@ 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 */; }; + 00733ECB2BFDD32300D53BA8 /* SpatialVideoDataWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00733ECA2BFDD32300D53BA8 /* SpatialVideoDataWriter.swift */; }; + 00733ECD2BFDDDE100D53BA8 /* CCSpatialShootController+GenerateImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00733ECC2BFDDDE100D53BA8 /* CCSpatialShootController+GenerateImage.swift */; }; + 00733ECF2BFDDE2300D53BA8 /* CCSpatialShootController+GenerateVideo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00733ECE2BFDDE2300D53BA8 /* CCSpatialShootController+GenerateVideo.swift */; }; + 00733EDF2BFF553100D53BA8 /* SVDWStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00733EDE2BFF553100D53BA8 /* SVDWStack.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 */; }; @@ -141,6 +145,10 @@ 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 = ""; }; + 00733ECA2BFDD32300D53BA8 /* SpatialVideoDataWriter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpatialVideoDataWriter.swift; sourceTree = ""; }; + 00733ECC2BFDDDE100D53BA8 /* CCSpatialShootController+GenerateImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CCSpatialShootController+GenerateImage.swift"; sourceTree = ""; }; + 00733ECE2BFDDE2300D53BA8 /* CCSpatialShootController+GenerateVideo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CCSpatialShootController+GenerateVideo.swift"; sourceTree = ""; }; + 00733EDE2BFF553100D53BA8 /* SVDWStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SVDWStack.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 = ""; }; @@ -322,6 +330,8 @@ 00733EA82BFB462500D53BA8 /* CCSpatialShootController+SessionConfigure.swift */, 00733EAA2BFB471100D53BA8 /* CCSpatialShootController+CaputreAction.swift */, 00733EAC2BFB47AE00D53BA8 /* CCSpatialShootController+Generate.swift */, + 00733ECE2BFDDE2300D53BA8 /* CCSpatialShootController+GenerateVideo.swift */, + 00733ECC2BFDDDE100D53BA8 /* CCSpatialShootController+GenerateImage.swift */, ); path = CCSpatialShootController; sourceTree = ""; @@ -414,6 +424,8 @@ 1E1EA28F2B933C8200A5D5D2 /* VideoWriter.swift */, 00D33BF92B9AB21A00604A44 /* ZZHAVExtension.swift */, 1EE5C5F92B8F97BF00EDFC2F /* SpatialVideoWriter.swift */, + 00733ECA2BFDD32300D53BA8 /* SpatialVideoDataWriter.swift */, + 00733EDE2BFF553100D53BA8 /* SVDWStack.swift */, 005580772B9F1525004B9567 /* ZZHHelper.swift */, 00ED6B332BA04AC200915BDE /* PlayByTransferConvertor.swift */, 00374AE02BC92B7C00F1F20F /* ZNetUtil.swift */, @@ -845,6 +857,7 @@ 00B946252B67B7DE00DA668F /* CCSpatialPlayView.swift in Sources */, 0096624A2BB3B45200FCA65F /* ExternalSceneDelegate.swift in Sources */, AFD9F5932B58C34A008716DE /* ImageProcessingShaders.metal in Sources */, + 00733ECF2BFDDE2300D53BA8 /* CCSpatialShootController+GenerateVideo.swift in Sources */, AF2120F02B4EA39D00400B7F /* BaseTableViewGroupedController.swift in Sources */, 1E1EA2962B936C9600A5D5D2 /* VideoConvertor2.swift in Sources */, AF2120CA2B4E95DA00400B7F /* UITableView+Add.swift in Sources */, @@ -852,6 +865,7 @@ 009DFB0E2BC8CFA2007B56E8 /* FeedbackView.swift in Sources */, 00BD87862BDE595F0014E8B3 /* CCSpatialPhotoDisplayEx.swift in Sources */, AF2120C42B4E95DA00400B7F /* UIImage+Add.swift in Sources */, + 00733ECD2BFDDDE100D53BA8 /* CCSpatialShootController+GenerateImage.swift in Sources */, 1EFAF0C02B8B7A59002A1773 /* VRPhotoTransformController.swift in Sources */, AF2120D82B4E9AC500400B7F /* CCAddImageView.swift in Sources */, 00D33BF42B998BF700604A44 /* SpatialImageConvertor.swift in Sources */, @@ -870,6 +884,7 @@ AF2121092B4EA7E200400B7F /* CCRequestDefine.swift in Sources */, 005580782B9F1525004B9567 /* ZZHHelper.swift in Sources */, AF2120C32B4E95DA00400B7F /* NSObject+Add.swift in Sources */, + 00733EDF2BFF553100D53BA8 /* SVDWStack.swift in Sources */, 1EE5C5F72B8F973A00EDFC2F /* CCSpatialShootController.swift in Sources */, 006B61DE2BBCFB45003FCB49 /* CustomSheetCell.swift in Sources */, 0073BD142BCE80F700721885 /* ZZHCustomPlayer.swift in Sources */, @@ -915,6 +930,7 @@ 1E02C9322B8990C600DD3143 /* CCDeviceOperationListView.swift in Sources */, 00BD87972BE10B800014E8B3 /* DisplayLinkProxy.swift in Sources */, AF2120DA2B4E9BD400400B7F /* CCAlert.swift in Sources */, + 00733ECB2BFDD32300D53BA8 /* SpatialVideoDataWriter.swift in Sources */, 006B61D12BBA5DB4003FCB49 /* MembershipProductView.swift in Sources */, 1EFB8C702B88DA4800C72119 /* CCBottomMenuCell.swift in Sources */, AF2120FA2B4EA5BD00400B7F /* CCHomeController.swift in Sources */, diff --git a/SwiftProject/SwiftProject.xcworkspace/xcuserdata/aaa.xcuserdatad/UserInterfaceState.xcuserstate b/SwiftProject/SwiftProject.xcworkspace/xcuserdata/aaa.xcuserdatad/UserInterfaceState.xcuserstate index 3c06a1c..8561a69 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 a2c2a87..446c833 100644 --- a/SwiftProject/SwiftProject.xcworkspace/xcuserdata/aaa.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/SwiftProject/SwiftProject.xcworkspace/xcuserdata/aaa.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -505,22 +505,6 @@ landmarkType = "7"> - - - - UIImage? { guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return nil } @@ -109,195 +116,44 @@ extension CCSpatialShootController: AVCaptureAudioDataOutputSampleBufferDelegate - - - - - - - - - - - - - - - - - - - - - - - - - - - - //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 removeExistingFile(at outputVideoURL: URL) { + if FileManager.default.fileExists(atPath: outputVideoURL.path) { + do { + try FileManager.default.removeItem(atPath: outputVideoURL.path) + print("视频文件删除成功") + } catch { + print("删除视频文件出错:\(error)") + } + } } - //保存照片 - 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+GenerateImage.swift b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialShootController/CCSpatialShootController+GenerateImage.swift new file mode 100644 index 0000000..1e80c17 --- /dev/null +++ b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialShootController/CCSpatialShootController+GenerateImage.swift @@ -0,0 +1,137 @@ +// +// CCSpatialShootController+GenerateImage.swift +// SwiftProject +// +// Created by aaa on 2024/5/22. +// + +import Foundation +import AVFoundation +import Photos +import AVKit +import VideoToolbox +import SVProgressHUD +import Firebase +import CoreMotion + +extension CCSpatialShootController { + + + + //合成空间图片 + 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+GenerateVideo.swift b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialShootController/CCSpatialShootController+GenerateVideo.swift new file mode 100644 index 0000000..01911ce --- /dev/null +++ b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialShootController/CCSpatialShootController+GenerateVideo.swift @@ -0,0 +1,35 @@ +// +// CCSpatialShootController+GenerateVideo.swift +// SwiftProject +// +// Created by aaa on 2024/5/22. +// + +import Foundation +import Photos + +extension CCSpatialShootController { + + + + + //MARK: 空间视频合成 +// func createSpVideo(vd:ZVideoData){ +// svdWriter.writeVideoData(leftSamplebuffer: vd.leftSampleBuffer!, rightSamplebuffer: vd.rightSampleBuffer!, audioSamplebuffer: vd.audioSampleBuffer!) +// } + + 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("保存失败") + } + } + } + + +} diff --git a/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialShootController/CCSpatialShootController.swift b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialShootController/CCSpatialShootController.swift index dd3c3e3..be19a45 100644 --- a/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialShootController/CCSpatialShootController.swift +++ b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialShootController/CCSpatialShootController.swift @@ -13,8 +13,25 @@ import VideoToolbox import SVProgressHUD import Firebase import CoreMotion + +//class ZVideoData:NSObject { +// var leftSampleBuffer:CMSampleBuffer? +// var rightSampleBuffer:CMSampleBuffer? +// var audioSampleBuffer:CMSampleBuffer? +// var isReady:Bool { +// get { +// return leftSampleBuffer != nil && rightSampleBuffer != nil && audioSampleBuffer != nil +// } +// } +// +// override init() { +// super.init() +// } +//} + class CCSpatialShootController: BaseController { + let writeVideoQueuen = dispatch_queue_serial_t(label: "ccspatial writeVideoQueuen")//创建一个串行队列 let kNowTimeToUserDefaultKey_SpatialShootController:String = "kNowTimeToUserDefaultKey_SpatialShootController" var wideAngleCameraDevice:AVCaptureDevice?//测试使用 @@ -22,6 +39,14 @@ class CCSpatialShootController: BaseController { var isTakePhoto_ultraCamera = false var isTakePhoto_wideCamera = false let takePhotoSemaphore = DispatchSemaphore(value: 1) + var isRecordingVideoData = false //是否正在录像 +// var videoData:ZVideoData = ZVideoData() + lazy var svdWriter:SpatialVideoDataWriter = { + let writer = SpatialVideoDataWriter() + writer.outputVideoURL = outputVideoURL + return writer + + }() //AVCaptureSession --- 单摄像头 var session = AVCaptureMultiCamSession()//多摄像头 @@ -47,10 +72,10 @@ class CCSpatialShootController: BaseController { var leftEyeVideoURL:URL? var rightEyeVideoURL:URL? - var outputVideoURL: URL? + let outputVideoURL: URL = URL.documentsDirectory.appendingPathComponent("output.MOV") let motionManager = CMMotionManager() var imgs:NSMutableArray = NSMutableArray() //存放广角、主摄照片数组 - let spatialVideoWriter = SpatialVideoWriter() +// let spatialVideoWriter = SpatialVideoWriter() //================================ //当前相机模式 @@ -244,7 +269,6 @@ class CCSpatialShootController: BaseController { super.viewDidLoad() ZZHHelper.setNowTimeToUserDefaultWithKey(kNowTimeToUserDefaultKey_SpatialShootController) - outputVideoURL = URL.documentsDirectory.appendingPathComponent("output.MOV") configureSession() // 设置相机参数 setUI() diff --git a/SwiftProject/SwiftProject/Project/Util/SVDWStack.swift b/SwiftProject/SwiftProject/Project/Util/SVDWStack.swift new file mode 100644 index 0000000..c4c4cfe --- /dev/null +++ b/SwiftProject/SwiftProject/Project/Util/SVDWStack.swift @@ -0,0 +1,31 @@ +// +// SVDWStack.swift +// SwiftProject +// +// Created by aaa on 2024/5/23. +// + +import Foundation +struct SVDWStack { + private var storage: [Element] = [] + + mutating func push(_ element: Element) { + storage.append(element) + } + + mutating func pop() -> Element? { + return storage.popLast() + } + + func top() -> Element? { + return storage.last + } + + func isEmpty() -> Bool { + return storage.isEmpty + } + + mutating func clear() { + storage.removeAll() + } +} diff --git a/SwiftProject/SwiftProject/Project/Util/SpatialVideoDataWriter.swift b/SwiftProject/SwiftProject/Project/Util/SpatialVideoDataWriter.swift new file mode 100644 index 0000000..1ce95df --- /dev/null +++ b/SwiftProject/SwiftProject/Project/Util/SpatialVideoDataWriter.swift @@ -0,0 +1,184 @@ +// +// SpatialVideoWriter.swift +// tdvideo +// +// Created by mac on 2024/2/22. +//。两个普通视频合成空间视频 + +import UIKit +import AVFoundation +import VideoToolbox +import Photos + +class SpatialVideoDataWriter { + + var assetWriter:AVAssetWriter! + var videoSettings: [String: Any]! + var input_video:AVAssetWriterInput! + let inputSettings_Audio = [ + AVFormatIDKey: kAudioFormatLinearPCM, // 指定未压缩格式 + AVSampleRateKey: 44100, + AVNumberOfChannelsKey: 2, + AVLinearPCMIsBigEndianKey:true, + AVLinearPCMIsFloatKey:true, + AVLinearPCMBitDepthKey:32, + AVLinearPCMIsNonInterleaved:false, + ] as [String:Any] + + var writerInput_Audio_left:AVAssetWriterInput! + var adaptor_inputVideo:AVAssetWriterInputTaggedPixelBufferGroupAdaptor! + + var isReady:Bool { + get { + return assetWriter != nil + } + } + + var haveStartedSession:Bool = false + + var outputVideoURL:URL! + + var leftSampleBuffer:SVDWStack = SVDWStack() + var rightSampleBuffer:SVDWStack = SVDWStack() + var audioSmapleBuffer:SVDWStack = SVDWStack() + + + //MARK: - Function实现 + func exAddLeftSampleBuffer(_ buffer:CMSampleBuffer) { + initWriterWithSmapleBuffer(buffer) + leftSampleBuffer.push(buffer) + autoWriteData() + } + + func exAddRightSampleBuffer(_ buffer:CMSampleBuffer) { + initWriterWithSmapleBuffer(buffer) + rightSampleBuffer.push(buffer) + autoWriteData() + } + + func exAddAudioSampleBuffer(_ buffer:CMSampleBuffer) { + audioSmapleBuffer.push(buffer) + autoWriteData() + } + + //开始自动写入 + func autoWriteData(){ + if !leftSampleBuffer.isEmpty() && !rightSampleBuffer.isEmpty() && !audioSmapleBuffer.isEmpty() { + writeVideoData(leftSamplebuffer: leftSampleBuffer.pop()!, rightSamplebuffer: rightSampleBuffer.pop()!, audioSamplebuffer: audioSmapleBuffer.pop()!) + } + } + + func initWriterWithSmapleBuffer(_ buffer:CMSampleBuffer){ + if !self.isReady { + let pixelBuffer = CMSampleBufferGetImageBuffer(buffer) + if let pixelBuffer { + let videoWidth = CVPixelBufferGetWidth(pixelBuffer) + let videoHeight = CVPixelBufferGetHeight(pixelBuffer) + writeVideoDataInit(videoWidth: KScreenHeight, videoHeight: KScreenWidth) + } + } + } + + + + //第一步,写入之前应当先初始化配置信息 + func writeVideoDataInit(videoWidth:Double,videoHeight:Double) { + do { + // 创建视频输入 + assetWriter = try AVAssetWriter(outputURL: outputVideoURL, fileType: .mov) + videoSettings = [ + AVVideoWidthKey: videoWidth, + AVVideoHeightKey: videoHeight, + AVVideoCodecKey: AVVideoCodecType.hevc, + AVVideoCompressionPropertiesKey: [ + kVTCompressionPropertyKey_MVHEVCVideoLayerIDs: [0, 1] as CFArray, + kCMFormatDescriptionExtension_HorizontalFieldOfView: 90_000, // asset-specific, in thousandths of a degree + kVTCompressionPropertyKey_HorizontalDisparityAdjustment: 200, // asset-specific + ] + ] + input_video = AVAssetWriterInput(mediaType: .video, outputSettings: videoSettings) + input_video.expectsMediaDataInRealTime = true + adaptor_inputVideo = AVAssetWriterInputTaggedPixelBufferGroupAdaptor(assetWriterInput: input_video) + assetWriter.add(input_video) + + // 创建音频输入 + writerInput_Audio_left = AVAssetWriterInput.init(mediaType: .audio, outputSettings: inputSettings_Audio) + writerInput_Audio_left.expectsMediaDataInRealTime = true + if assetWriter.canAdd(writerInput_Audio_left) { + assetWriter.add(writerInput_Audio_left) + print("assetWriter 添加writerInput_Audio_left成功...") + } + else { + print("assetWriter 添加writerInput_Audio_left失败...") + } + assetWriter.startWriting() + assetWriter.startSession(atSourceTime: .zero) + + } + catch { + print("writeVideoDataInit 初始化遇到问题:\(error)") + } + } + + //第三部,写入完毕之后应当释放配置信息 + func writeVideoDataDeInit(completion: @escaping (Bool, Error?) -> Void) { + writerInput_Audio_left.markAsFinished() + input_video.markAsFinished() + assetWriter.finishWriting { + print("assetWriter.finishWriting 可以保存") + completion(true, nil) + } + haveStartedSession = false + assetWriter = nil + input_video = nil + adaptor_inputVideo = nil + writerInput_Audio_left = nil + + } + + //第二步,开始写入数据 + func writeVideoData(leftSamplebuffer:CMSampleBuffer,rightSamplebuffer:CMSampleBuffer,audioSamplebuffer:CMSampleBuffer) { + + //追加视频帧 + guard let leftCVPixelBuffer = CMSampleBufferGetImageBuffer(leftSamplebuffer) , + let rightCVPixelBuffer = CMSampleBufferGetImageBuffer(rightSamplebuffer) else { + print("获取左右眼像素缓冲区失败") + return + } + + + if ( !haveStartedSession ) { + assetWriter.startSession(atSourceTime: CMSampleBufferGetPresentationTimeStamp(leftSamplebuffer)) + haveStartedSession = true + } + + // 创建 CMTaggedBuffer 数组,包含左右眼视图的像素缓冲区 + + let left = CMTaggedBuffer(tags: [.stereoView(.leftEye), .videoLayerID(0)], pixelBuffer: leftCVPixelBuffer) + let right = CMTaggedBuffer(tags: [.stereoView(.rightEye), .videoLayerID(1)], pixelBuffer: rightCVPixelBuffer) + + while !adaptor_inputVideo.assetWriterInput.isReadyForMoreMediaData { + // 等待直到 writerInput 准备好接收下一个视频帧 + Thread.sleep(forTimeInterval: 0.1) // 暂停一段时间,避免过度占用资源 + } + + let appendResult = adaptor_inputVideo.appendTaggedBuffers([left, right], withPresentationTime: leftSamplebuffer.presentationTimeStamp) + print("appendVideoImage samplebuffer Frame Result :\(appendResult)") + + + //追加音频帧 + if writerInput_Audio_left.isReadyForMoreMediaData { + if writerInput_Audio_left.append(audioSamplebuffer) == false { + print("追加音频失败.....") + } + else{ + print("audio 追加成功....") + } + } + else { + print("audio 追加还未准备好...") + + } + + } +}