diff --git a/SwiftProject/SwiftProject.xcodeproj/project.pbxproj b/SwiftProject/SwiftProject.xcodeproj/project.pbxproj index 2bc877c..31b4176 100644 --- a/SwiftProject/SwiftProject.xcodeproj/project.pbxproj +++ b/SwiftProject/SwiftProject.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ 006B61DE2BBCFB45003FCB49 /* CustomSheetCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006B61DD2BBCFB45003FCB49 /* CustomSheetCell.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 */; }; 009661F82BAD6C7100FCA65F /* CCSpaceAlbumFilterPopView2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 009661F72BAD6C7100FCA65F /* CCSpaceAlbumFilterPopView2.swift */; }; 009661FA2BAD876200FCA65F /* PhotosUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 009661F92BAD876200FCA65F /* PhotosUI.framework */; }; 009661FC2BADB20D00FCA65F /* CoreMotion.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 009661FB2BADB20D00FCA65F /* CoreMotion.framework */; }; @@ -28,7 +29,6 @@ 009662462BB3B39900FCA65F /* External.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 009662452BB3B39900FCA65F /* External.storyboard */; }; 0096624A2BB3B45200FCA65F /* ExternalSceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 009662492BB3B45200FCA65F /* ExternalSceneDelegate.swift */; }; 0096624D2BB3BA3B00FCA65F /* ZZHExternalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0096624C2BB3BA3B00FCA65F /* ZZHExternalViewController.swift */; }; - 0096625A2BB552E700FCA65F /* CCSpatialVideoDisplayForVideoTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 009662592BB552E700FCA65F /* CCSpatialVideoDisplayForVideoTask.swift */; }; 009DFB032BC63AF0007B56E8 /* CoreImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 009DFB022BC63AF0007B56E8 /* CoreImage.framework */; }; 009DFB0E2BC8CFA2007B56E8 /* FeedbackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 009DFB0D2BC8CFA2007B56E8 /* FeedbackView.swift */; }; 009DFB112BC8E2E9007B56E8 /* MenuVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 009DFB102BC8E2E9007B56E8 /* MenuVC.swift */; }; @@ -130,6 +130,9 @@ 006B61DD2BBCFB45003FCB49 /* CustomSheetCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomSheetCell.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 = ""; }; + 0073BD1B2BCFCC4500721885 /* CCSpatialVideoDisplayController1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CCSpatialVideoDisplayController1.swift; sourceTree = ""; }; + 0073BD1D2BCFCC6900721885 /* CCSpatialVideoDisplayForVideoTask1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CCSpatialVideoDisplayForVideoTask1.swift; sourceTree = ""; }; 009661F72BAD6C7100FCA65F /* CCSpaceAlbumFilterPopView2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CCSpaceAlbumFilterPopView2.swift; sourceTree = ""; }; 009661F92BAD876200FCA65F /* PhotosUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PhotosUI.framework; path = System/Library/Frameworks/PhotosUI.framework; sourceTree = SDKROOT; }; 009661FB2BADB20D00FCA65F /* CoreMotion.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMotion.framework; path = System/Library/Frameworks/CoreMotion.framework; sourceTree = SDKROOT; }; @@ -137,7 +140,6 @@ 009662452BB3B39900FCA65F /* External.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = External.storyboard; sourceTree = ""; }; 009662492BB3B45200FCA65F /* ExternalSceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalSceneDelegate.swift; sourceTree = ""; }; 0096624C2BB3BA3B00FCA65F /* ZZHExternalViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZZHExternalViewController.swift; sourceTree = ""; }; - 009662592BB552E700FCA65F /* CCSpatialVideoDisplayForVideoTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CCSpatialVideoDisplayForVideoTask.swift; sourceTree = ""; }; 009DFB022BC63AF0007B56E8 /* CoreImage.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreImage.framework; path = System/Library/Frameworks/CoreImage.framework; sourceTree = SDKROOT; }; 009DFB0D2BC8CFA2007B56E8 /* FeedbackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackView.swift; sourceTree = ""; }; 009DFB102BC8E2E9007B56E8 /* MenuVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuVC.swift; sourceTree = ""; }; @@ -285,6 +287,15 @@ path = CustomSheetController; sourceTree = ""; }; + 0072361D2BD0EEFC000595A9 /* deapt */ = { + isa = PBXGroup; + children = ( + 0073BD1B2BCFCC4500721885 /* CCSpatialVideoDisplayController1.swift */, + 0073BD1D2BCFCC6900721885 /* CCSpatialVideoDisplayForVideoTask1.swift */, + ); + path = deapt; + sourceTree = ""; + }; 0096624B2BB3BA0100FCA65F /* ExternalScreen */ = { isa = PBXGroup; children = ( @@ -298,9 +309,10 @@ 009662582BB5527200FCA65F /* CCSpatialVideoDisplayController */ = { isa = PBXGroup; children = ( + 0072361D2BD0EEFC000595A9 /* deapt */, 1E1EA2932B9364F000A5D5D2 /* CCSpatialVideoDisplayController.swift */, - 009662592BB552E700FCA65F /* CCSpatialVideoDisplayForVideoTask.swift */, 0073BD132BCE80F700721885 /* ZZHCustomPlayer.swift */, + 0073BD192BCFC8E800721885 /* ZZHCustomPlayerForVideoTask.swift */, 0073BD172BCF7B3400721885 /* ZZHCustomSlider.swift */, ); path = CCSpatialVideoDisplayController; @@ -784,6 +796,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 0073BD1A2BCFC8E800721885 /* ZZHCustomPlayerForVideoTask.swift in Sources */, AF2120CC2B4E973800400B7F /* CCInputTextField.swift in Sources */, AF2120C72B4E95DA00400B7F /* UIColor+Add.swift in Sources */, AF2120C52B4E95DA00400B7F /* UIButton+Add.swift in Sources */, @@ -863,7 +876,6 @@ 1E02C9322B8990C600DD3143 /* CCDeviceOperationListView.swift in Sources */, AF2120DA2B4E9BD400400B7F /* CCAlert.swift in Sources */, 006B61D12BBA5DB4003FCB49 /* MembershipProductView.swift in Sources */, - 0096625A2BB552E700FCA65F /* CCSpatialVideoDisplayForVideoTask.swift in Sources */, 1EFB8C702B88DA4800C72119 /* CCBottomMenuCell.swift in Sources */, AF2120FA2B4EA5BD00400B7F /* CCHomeController.swift in Sources */, 0073BD182BCF7B3400721885 /* ZZHCustomSlider.swift in Sources */, diff --git a/SwiftProject/SwiftProject.xcworkspace/xcuserdata/aaa.xcuserdatad/UserInterfaceState.xcuserstate b/SwiftProject/SwiftProject.xcworkspace/xcuserdata/aaa.xcuserdatad/UserInterfaceState.xcuserstate index 9446302..010a39e 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 1dd7fe4..f7893bb 100644 --- a/SwiftProject/SwiftProject.xcworkspace/xcuserdata/aaa.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/SwiftProject/SwiftProject.xcworkspace/xcuserdata/aaa.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -515,9 +515,9 @@ filePath = "SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/CCSpatialVideoDisplayController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "388" - endingLineNumber = "388" - landmarkName = "viewDidAppear(_:)" + startingLineNumber = "340" + endingLineNumber = "340" + landmarkName = "notification_didActiveNotification(notification:)" landmarkType = "7"> @@ -531,9 +531,9 @@ filePath = "SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/CCSpatialVideoDisplayController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "552" - endingLineNumber = "552" - landmarkName = "displayUpdate(caDisplayLink:)" + startingLineNumber = "498" + endingLineNumber = "498" + landmarkName = "updateTopCenterButtonWhenIsPlayingChange()" landmarkType = "7"> + + diff --git a/SwiftProject/SwiftProject/Assets.xcassets/customPlayer/custom_playBtn.imageset/Contents.json b/SwiftProject/SwiftProject/Assets.xcassets/customPlayer/custom_playBtn.imageset/Contents.json index e082eff..222061f 100644 --- a/SwiftProject/SwiftProject/Assets.xcassets/customPlayer/custom_playBtn.imageset/Contents.json +++ b/SwiftProject/SwiftProject/Assets.xcassets/customPlayer/custom_playBtn.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "icon@1x.png", + "filename" : "play.png", "idiom" : "universal", "scale" : "1x" }, diff --git a/SwiftProject/SwiftProject/Assets.xcassets/customPlayer/custom_playBtn.imageset/icon@1x.png b/SwiftProject/SwiftProject/Assets.xcassets/customPlayer/custom_playBtn.imageset/icon@1x.png deleted file mode 100644 index 45cd00b..0000000 Binary files a/SwiftProject/SwiftProject/Assets.xcassets/customPlayer/custom_playBtn.imageset/icon@1x.png and /dev/null differ diff --git a/SwiftProject/SwiftProject/Assets.xcassets/customPlayer/custom_playBtn.imageset/play.png b/SwiftProject/SwiftProject/Assets.xcassets/customPlayer/custom_playBtn.imageset/play.png new file mode 100644 index 0000000..edce10d Binary files /dev/null and b/SwiftProject/SwiftProject/Assets.xcassets/customPlayer/custom_playBtn.imageset/play.png differ diff --git a/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/CCSpatialVideoDisplayController.swift b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/CCSpatialVideoDisplayController.swift index 097cfe3..796ccbf 100644 --- a/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/CCSpatialVideoDisplayController.swift +++ b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/CCSpatialVideoDisplayController.swift @@ -54,9 +54,7 @@ class CCSpatialVideoDisplayController: BaseController { (icon:"type_check",title:NSLocalizedString("交叉眼", comment: ""),isHiden:false)] } //更新串流UI -// DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: { - self.setttinisScreenMirroring(isScreenMirroring: self.isAirPlayActive) -// }) + self.setttinisScreenMirroring(isScreenMirroring: self.isAirPlayActive) } @@ -77,12 +75,7 @@ class CCSpatialVideoDisplayController: BaseController { return label }() - var assetReader:AVAssetReader? - - var assetOutput:AVAssetReaderTrackOutput? - - var assetTrack:AVAssetTrack? - + let kNowTimeToUserDefaultKey_VideoDisplayController:String = "kNowTimeToUserDefaultKey_VideoDisplayController" var link = false//是否已连接设备 var isPlaying = false {//是否正在串流播放 @@ -95,17 +88,18 @@ class CCSpatialVideoDisplayController: BaseController { { didSet{ //进行相应解码操作,边解边播 + customPlayer.selectedIndex = selectedIndex } } - let videoTranserConvertor = PlayByTransferConvertor() +// let videoTranserConvertor = PlayByTransferConvertor() - lazy var videoOriginalAsset:AVAsset = { - let asset = AVAsset(url: sourceVideoURL!) - return asset - }() +// lazy var videoOriginalAsset:AVAsset = { +// let asset = AVAsset(url: sourceVideoURL!) +// return asset +// }() var videoOriginalPHAsset:PHAsset? @@ -113,13 +107,16 @@ class CCSpatialVideoDisplayController: BaseController { var sourceVideoURL:URL? var imgData:Data? - var player:AVPlayer? +// var player:AVPlayer? // var needRestReaderTimeRange = false//标识是否需呀重置时间读取范围 - var playerController:AVPlayerViewController? +// var playerController:AVPlayerViewController? + var customPlayer:ZZHCustomPlayer = { + let cp = ZZHCustomPlayer(frame: .zero) + return cp + }() lazy var mTopImgView:UIImageView = { //393*236 -// let view = UIImageView(frame: CGRect(x: 0, y: 0, width: SCREEN_Width, height: SCREEN_Height * 236/393)) let view = UIImageView(frame: CGRect(x: 0, y: 0, width: SCREEN_Width, height: 236)) view.image = UIImage.init(named: "BG_Top") @@ -218,9 +215,6 @@ class CCSpatialVideoDisplayController: BaseController { let img2:UIImage = UIImage.init(named: NSLocalizedString("displayvc_desc", comment: ""))! button.setImage(img2, for: UIControl.State.normal) button.frame = CGRect(x: 0, y: 0, width: 173, height: 42) -// button.titleLabel?.font = KFont_Regular(14) -// button.titleLabel?.adjustsFontSizeToFitWidth = true -// button.updateBtnEdgeInsets(style: .Left, space: 8) button.centerY = progressView.top - 70 button.centerX = self.view.centerX @@ -229,36 +223,13 @@ class CCSpatialVideoDisplayController: BaseController { //监听播放完毕 - @objc func notification_PlayerEndTime(notification:Notification){ - print("PlayerEndTime....") - player?.seek(to: .zero) - player?.play() -// -// self.playerController?.view.layer.sublayers?.forEach({ clayer in -// print("clayer.......\(clayer.self)") -// clayer.backgroundColor = UIColor.red.cgColor -// clayer.sublayers?.forEach({ sclayer in -// print("sclayer.......\(sclayer.self)") -// sclayer.backgroundColor = UIColor.blue.cgColor -// sclayer.sublayers?.forEach({ sscalyer in -// print("sscalyer.......\(sscalyer.self)") -// if let avPlayerLayer = sscalyer as? AVPlayerLayer { -// sscalyer.backgroundColor = UIColor.orange.cgColor -// sscalyer.sublayers?.forEach({ avSubLayer in -// print("avSubLayer.......\(avSubLayer.self)") -// avSubLayer.backgroundColor = UIColor.systemPink.cgColor -// if let avaPlayerLayer2 = avSubLayer as? AVPlayerLayer { -// avaPlayerLayer2.backgroundColor = UIColor.white.cgColor -// -// } -// }) -// } -// -// }) -// }) -// }) +// @objc func notification_PlayerEndTime(notification:Notification){ +// print("PlayerEndTime....") +// player?.seek(to: .zero) +// player?.play() - } + +// } //监听拖动播放的进度事件 // @objc func notification_PlayerTimeJump(notification:Notification){ @@ -273,38 +244,41 @@ class CCSpatialVideoDisplayController: BaseController { super.viewDidLoad() configureUI() ZZHHelper.setNowTimeToUserDefaultWithKey(kNowTimeToUserDefaultKey_VideoDisplayController) - Task { - print("ccs viewdid load ---------") - await self.loadVideoAssetReader() - DispatchQueue.main.asyncAfter(deadline: .now()+0.01) {[weak self] in - self?.setUPPlayer() - // 检查当前是否已连接到 AirPlay 设备 - self?.checkAirPlayStatus() - - - } - } +// Task { +// print("ccs viewdid load ---------") +// await self.loadVideoAssetReader() +// DispatchQueue.main.asyncAfter(deadline: .now()+0.01) {[weak self] in +// self?.setUPPlayer() +// +// +// +// +// } +// } + self.customPlayer.setUPPlayer() + // 检查当前是否已连接到 AirPlay 设备 + self.checkAirPlayStatus() } - func setUPPlayer(){ - print("getPlayerItem ========") - player = AVPlayer() - player?.allowsExternalPlayback = true - player?.usesExternalPlaybackWhileExternalScreenIsActive = true - - print("getPlayerItem >>>>>>>") - - playerController = AVPlayerViewController() - playerController?.player = player - playerController!.view.backgroundColor = .clear - playerController?.view.frame = CGRect.init(x: 0, y: 170, width: self.view.frame.size.width, height: 400) - - self.addChild(playerController!) - self.view.addSubview(playerController!.view) - - self.maskLabel.frame = playerController!.view.frame - self.view.addSubview(self.maskLabel) - } +// func setUPPlayer(){ +// print("getPlayerItem ========") +// player = AVPlayer() +// player?.allowsExternalPlayback = true +// player?.usesExternalPlaybackWhileExternalScreenIsActive = true +// +// print("getPlayerItem >>>>>>>") +// +// playerController = AVPlayerViewController() +// playerController?.player = player +// playerController!.view.backgroundColor = .clear +// playerController?.view.frame = CGRect.init(x: 0, y: 170, width: self.view.frame.size.width, height: 400) +// +// self.addChild(playerController!) +// self.view.addSubview(playerController!.view) +// +// self.maskLabel.frame = playerController!.view.frame +// self.view.addSubview(self.maskLabel) +// } func configureUI(){ @@ -322,7 +296,20 @@ class CCSpatialVideoDisplayController: BaseController { navtionBar?.addSubview(mTopCenterTypeButton) self.view.addSubview(tipsButton) + self.view.addSubview(self.customPlayer) + self.customPlayer.snp.makeConstraints { make in + make.left.right.centerY.equalToSuperview() + make.height.equalTo(460) + } + self.customPlayer.sourceVideoURL = sourceVideoURL + self.view.addSubview(self.maskLabel) + self.maskLabel.snp.makeConstraints { make in + make.left.equalTo(customPlayer.snp.left) + make.right.equalTo(customPlayer.snp.right) + make.top.equalTo(customPlayer.snp.top) + make.bottom.equalTo(customPlayer.snp.bottom) + } @@ -334,7 +321,7 @@ class CCSpatialVideoDisplayController: BaseController { - NotificationCenter.default.addObserver(self, selector: #selector(notification_PlayerEndTime(notification:)), name: AVPlayerItem.didPlayToEndTimeNotification, object: nil) +// NotificationCenter.default.addObserver(self, selector: #selector(notification_PlayerEndTime(notification:)), name: AVPlayerItem.didPlayToEndTimeNotification, object: nil) // NotificationCenter.default.addObserver(self, selector: #selector(notification_PlayerTimeJump(notification:)), name: AVPlayerItem.timeJumpedNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(notificatin_failedToPlayToEndTimeNotification(notifiation: )), name: AVPlayerItem.failedToPlayToEndTimeNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(notificatin_failedToPlayToEndTimeNotification(notifiation: )), name: AVPlayerItem.newErrorLogEntryNotification, object: nil) @@ -344,7 +331,8 @@ class CCSpatialVideoDisplayController: BaseController { //屏幕进入后台 @objc func notification_didEnterBackgroundNotification(notification:Notification) { - player?.pause() +// player?.pause() + customPlayer.play(false) print("ccs .....notification_didEnterBackgroundNotification") } @@ -370,7 +358,8 @@ class CCSpatialVideoDisplayController: BaseController { if externalVC != nil { externalVC?.playerLayer?.player = nil } - player?.pause() +// player?.pause() + customPlayer.play(false) releaseVideoComposition() let sec:TimeInterval = ZZHHelper.getSecFromUserDefaultByKey(kNowTimeToUserDefaultKey_VideoDisplayController) @@ -385,18 +374,18 @@ class CCSpatialVideoDisplayController: BaseController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - if self.player?.currentItem == nil {//此处需要判断是否为空:1,在didload的时候会设置一次,在viewDidDisappear的时候会被置空 - let playerItem = self.getPlayerItem() - self.player?.replaceCurrentItem(with: playerItem) - } - - if self.externalVC != nil {//恢复外接屏幕的视频播放 - if self.externalVC?.playerLayer?.player == nil { - self.activeSession() - self.externalVC?.playerLayer?.player = self.player - } - } - self.player?.play() +// if self.player?.currentItem == nil {//此处需要判断是否为空:1,在didload的时候会设置一次,在viewDidDisappear的时候会被置空 +// let playerItem = self.getPlayerItem() +// self.player?.replaceCurrentItem(with: playerItem) +// } +// +// if self.externalVC != nil {//恢复外接屏幕的视频播放 +// if self.externalVC?.playerLayer?.player == nil { +// self.activeSession() +// self.externalVC?.playerLayer?.player = self.player +// } +// } +// self.player?.play() } //MARK: - 监听设备投流 @@ -462,14 +451,11 @@ class CCSpatialVideoDisplayController: BaseController { //当外接屏幕连接时 func dealTaskWhenExternalScreenConnect(){ -// if link == true { -// return -// } - + link = true isPlaying = true - let playerItem = self.getPlayerItem() - player?.replaceCurrentItem(with: playerItem) +// let playerItem = self.getPlayerItem() +// player?.replaceCurrentItem(with: playerItem) print("外接屏幕已连接.....") activeSession() @@ -517,8 +503,8 @@ class CCSpatialVideoDisplayController: BaseController { //当外接屏幕断开时 func dealTaskWhenExternalScreenDisConnect(){ print("外接屏幕已断开.....") - let playerItem = self.getPlayerItem() - player?.replaceCurrentItem(with: playerItem) +// let playerItem = self.getPlayerItem() +// player?.replaceCurrentItem(with: playerItem) link = false isPlaying = false @@ -543,8 +529,8 @@ class CCSpatialVideoDisplayController: BaseController { //隐藏弹出列表尾部 menuView.showFooterView(isShow: false, showText: "") externalVC?.playerLayer?.player = nil - self.playerController?.player = player - player?.play() +// self.playerController?.player = player +// player?.play() externalVC = nil } @@ -553,9 +539,9 @@ class CCSpatialVideoDisplayController: BaseController { if has_exvc.playerLayer?.player == nil { has_exvc.imageView?.isHidden = true has_exvc.playerLayer?.isHidden = false - self.playerController?.player = nil - has_exvc.playerLayer?.player = player - player?.play() +// self.playerController?.player = nil +// has_exvc.playerLayer?.player = player +// player?.play() externalDispalylink?.invalidate() externalDispalylink = nil } @@ -567,7 +553,6 @@ class CCSpatialVideoDisplayController: BaseController { deinit { print("patialvideodisplaycontroler deinit......") releaseVideoComposition() - assetReader?.cancelReading() NotificationCenter.default.removeObserver(self) // player?.removeObserver(self, forKeyPath: "status") } @@ -577,8 +562,8 @@ class CCSpatialVideoDisplayController: BaseController { externalDispalylink?.invalidate() externalDispalylink = nil } - self.player?.currentItem?.videoComposition = nil - self.player?.replaceCurrentItem(with: nil) +// self.player?.currentItem?.videoComposition = nil +// self.player?.replaceCurrentItem(with: nil) } @@ -641,26 +626,28 @@ class CCSpatialVideoDisplayController: BaseController { isPlaying = false if self.externalVC != nil { externalVC?.playerLayer?.player = nil - player?.pause() +// player?.pause() + customPlayer.play(false) releaseVideoComposition() DispatchQueue.main.asyncAfter(deadline: .now()+0.15, execute: {[weak self] in - let playerItem = self?.getPlayerItem() - self?.player?.replaceCurrentItem(with: playerItem) - self?.playerController?.player = self?.player - self?.player?.play() +// let playerItem = self?.getPlayerItem() +// self?.player?.replaceCurrentItem(with: playerItem) +// self?.playerController?.player = self?.player +// self?.player?.play() + }) } } else {//开始串流 if self.externalVC != nil { - self.playerController?.player = nil - player?.pause() - releaseVideoComposition() - let playerItem = self.getPlayerItem() - player?.replaceCurrentItem(with: playerItem) - externalVC?.playerLayer?.player = player - player?.play() +// self.playerController?.player = nil +// player?.pause() +// releaseVideoComposition() +// let playerItem = self.getPlayerItem() +// player?.replaceCurrentItem(with: playerItem) +// externalVC?.playerLayer?.player = player +// player?.play() isPlaying = true } } diff --git a/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/ZZHCustomPlayer.swift b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/ZZHCustomPlayer.swift index d72241c..ab6e4cd 100644 --- a/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/ZZHCustomPlayer.swift +++ b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/ZZHCustomPlayer.swift @@ -9,12 +9,36 @@ import Foundation import UIKit import AVKit class ZZHCustomPlayer: UIView { + //视频资源 + var sourceVideoURL:URL? + var assetTrack:AVAssetTrack? + lazy var videoOriginalAsset:AVAsset = { + let asset = AVAsset(url: sourceVideoURL!) + return asset + }() + + var assetReader:AVAssetReader? + var assetOutput:AVAssetReaderTrackOutput? + + let videoTranserConvertor = PlayByTransferConvertor() + var selectedIndex:SpatialType = .parallelEyes//记录当前选择的菜单选项 + { + didSet{ + //进行相应解码操作,边解边播 + } + } + + + //ui var playerLayerBgView:UIView? var avPlayer:AVPlayer? var avPlayerLayer:AVPlayerLayer? var timeSlider:ZZHCustomSlider? var prePlayingState:Bool?//标记 在滑块拖动前,player的播放状态,以便在拖动完毕之后回复播放状态 - let playerPauseBgColor:UIColor = UIColor(r: 245, g: 245, b: 245, a: 0.2)//暂停时的背景颜色 + let playerPauseBgColor:UIColor = UIColor(r: 20, g: 20, b: 20, a: 0.2)//暂停时的背景颜色 + + //线程锁 +// let lock = NSLock() lazy var maskPlayerView:UIView? = {//播放按钮 背景 let bgView = UIView() @@ -24,7 +48,7 @@ class ZZHCustomPlayer: UIView { self.playStateImgView.snp.makeConstraints { make in make.centerX.centerY.equalToSuperview() - make.width.height.equalTo(40) + make.width.height.equalTo(80) } let btn = UIButton() @@ -42,8 +66,25 @@ class ZZHCustomPlayer: UIView { return imgView }() + deinit { + print("zzhcustomplayer deinit......") + releaseVideoComposition() + assetReader?.cancelReading() + NotificationCenter.default.removeObserver(self) + } + + func releaseVideoComposition() { + self.avPlayer?.currentItem?.videoComposition = nil + self.avPlayer?.replaceCurrentItem(with: nil) + } + override init(frame: CGRect) { super.init(frame: frame) + NotificationCenter.default.addObserver(self, selector: #selector(notification_PlayerEndTime(notification:)), name: AVPlayerItem.didPlayToEndTimeNotification, object: nil) + + + + let playerWidth = KScreenWidth let playerHeight = 200 playerLayerBgView = UIView() @@ -54,10 +95,14 @@ class ZZHCustomPlayer: UIView { make.height.equalTo(playerHeight) } avPlayer = AVPlayer() + avPlayer?.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: self.avPlayer?.currentItem?.duration.timescale ?? 600), queue: DispatchQueue.main, using: {[weak self] ct in + self?.updateSliderUI(ct: ct) + }) + avPlayerLayer = AVPlayerLayer(player: avPlayer) avPlayerLayer?.frame = CGRect(x: 0, y: 0, width: Int(playerWidth), height: playerHeight) playerLayerBgView!.layer.addSublayer(avPlayerLayer!) - playerLayerBgView?.backgroundColor = UIColor.orange + playerLayerBgView?.backgroundColor = UIColor.clear self.maskPlayerView?.frame = avPlayerLayer!.frame playerLayerBgView?.addSubview(maskPlayerView!) @@ -65,8 +110,8 @@ class ZZHCustomPlayer: UIView { timeSlider = ZZHCustomSlider(frame: .zero) - timeSlider?.processChangeValueCallback = { (value,isMoving) in - print("value:\(value) isMoving:\(isMoving)") + timeSlider?.processChangeValueCallback = {[weak self] (value,isMoving) in + self?.manualToSeekPlay(value:value,isMoving: isMoving) } self.addSubview(timeSlider!) timeSlider!.snp.makeConstraints { make in @@ -75,8 +120,57 @@ class ZZHCustomPlayer: UIView { make.bottom.equalToSuperview().offset(0) make.height.equalTo(120) } - - + } + + //通过slider进行seek播放时间点 + func manualToSeekPlay(value:Float,isMoving:Bool){ + if isMoving { + self.play(false) + } + else { + let totalSec = CMTimeGetSeconds((self.avPlayer?.currentItem?.duration)!) + var atSec = Float(totalSec) * value + var timeScale:CMTimeScale? = self.avPlayer?.currentItem?.duration.timescale + if let ts = timeScale { + atSec = atSec * Float(ts) + } + else { + timeScale = CMTimeScale(1) + } + let ct = CMTime(value:CMTimeValue(atSec) , timescale: timeScale!) + quickLoadAReaderWhenSeek(startCT: ct) + +// self.releaseVideoComposition() +// self.avPlayer?.replaceCurrentItem(with: self.getPlayerItem()) + + print("正在seek.....\(ct)") + self.avPlayer?.seek(to: ct,toleranceBefore:.zero,toleranceAfter: .zero, completionHandler: {[weak self] finished in + if finished { + self?.quickLoadAReaderWhenSeek(startCT: ct) + self?.play(true) + } + + print("seek result:\(finished)") + + }) + } + } + + //初始化播放器信息 + func setUPPlayer() { + Task {[weak self] in + await self?.loadVideoAssetReader() + self?.avPlayer?.replaceCurrentItem(with: self?.getPlayerItem()) + self?.play(true) + + } + } + + //重新状态播放 + private func reLoadPlay(){ + self.releaseVideoComposition() + self.avPlayer?.replaceCurrentItem(with: self.getPlayerItem()) + self.play(true) } //暂停或者播放 @@ -107,12 +201,24 @@ class ZZHCustomPlayer: UIView { updatePlayMaskView(value) } +// func seek(ct:CMTime) { +// self.avPlayer?.seek(to: ct) +// } + //根据状态更新播放器遮罩视图显示与隐藏 func updatePlayMaskView(_ value:Bool) { playStateImgView.isHidden = value self.maskPlayerView?.backgroundColor = value ? .clear : playerPauseBgColor } + //根据播放进度更新slider的 ui + func updateSliderUI(ct:CMTime) { + let sec = CMTimeGetSeconds(ct) + let totalSec = CMTimeGetSeconds((self.avPlayer?.currentItem?.duration)!) + let s = sec / totalSec + self.timeSlider?.exUpdateProcessValue(value: Float(s),currSec: Int(sec),totalSec: Int(totalSec)) + } + override func draw(_ rect: CGRect) { super.draw(rect) @@ -121,4 +227,25 @@ class ZZHCustomPlayer: UIView { required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + + + + //MARK: - 视频资源加载 + + + + //MARK: - 播放器控制 + + //监听播放完毕 + @objc func notification_PlayerEndTime(notification:Notification){ + print("PlayerEndTime....") +// self.quickLoadAReaderWhenReplayBack() + avPlayer?.seek(to: .zero,toleranceBefore: .zero,toleranceAfter: .zero,completionHandler: {[weak self] finish in + self?.quickLoadAReaderWhenReplayBack() + self?.avPlayer?.play() + }) + + + } } diff --git a/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/ZZHCustomPlayerForVideoTask.swift b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/ZZHCustomPlayerForVideoTask.swift new file mode 100644 index 0000000..ce0aa91 --- /dev/null +++ b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/ZZHCustomPlayerForVideoTask.swift @@ -0,0 +1,162 @@ +// +// ZZHCustomPlayerForVideoTask.swift +// SwiftProject +// +// Created by aaa on 2024/4/17. +// + +import Foundation +import AVFoundation +import Photos +import AVKit +import VideoToolbox + +extension ZZHCustomPlayer { + func getPlayerItem() -> AVPlayerItem { + let temItem = AVPlayerItem(asset: self.videoOriginalAsset) + AVVideoComposition.videoComposition(with: temItem.asset) { [weak self] request in + + print("正在请求解码图片frame....") + guard let weakSelf = self else { + print("self 被销毁了.....") + return + } +// self?.lock.lock() + weakSelf.convertFrame(request:request) +// self?.lock.unlock() + + } completionHandler: { ac, err in + if err != nil { + print("初始化coposition报错:\(err)") + } + else{ + print("composition 生成ok....") + temItem.videoComposition = ac + } + } + + return temItem + } + + + func convertFrame(request:AVAsynchronousCIImageFilteringRequest){ + let compositionTime = request.compositionTime + let end:CMTime = CMTimeMake(value: Int64(compositionTime.value+1), timescale: compositionTime.timescale) + let tr = CMTimeRange(start: compositionTime, end: end) + + var ciImg:CIImage? = nil + switch self.selectedIndex { + case .monocular2D://双眼2d + ciImg = request.sourceImage + break + + default : + ciImg = self.otherModeImgWithMode(mode: self.selectedIndex,tr:tr,compositionTime: compositionTime) + break + } + if let ciImg { + request.finish(with: ciImg, context: nil) + } + else { + print("未合成成功.....") + request.finish(with: request.sourceImage, context: nil) + } + } + + func otherModeImgWithMode(mode:SpatialType,tr:CMTimeRange,compositionTime:CMTime)->CIImage? { + guard let ao = self.assetOutput else { + print("assetOutput 应该是没有被创建成功.....") + return nil + } + + var ciImg:CIImage? = nil + switch mode { + case .crossedEyes://交叉眼 + ciImg = self.videoTranserConvertor.convertVideo(asset: self.videoOriginalAsset, assetOutput: ao, type: self.selectedIndex, time: compositionTime) + break + + + case .fsbs://3d全宽 + ciImg = self.videoTranserConvertor.convertVideo(asset: self.videoOriginalAsset, assetOutput: ao, type: self.selectedIndex, time: compositionTime) + break + + + case .hsbs://3d半宽 + ciImg = self.videoTranserConvertor.convertVideo(asset: self.videoOriginalAsset, assetOutput: ao, type: self.selectedIndex, time: compositionTime) + break + + + case .parallelEyes://平行眼 + ciImg = self.videoTranserConvertor.convertVideo(asset: self.videoOriginalAsset, assetOutput: ao, type: self.selectedIndex, time: compositionTime) + break + + case .redBlueSolid://红蓝立体 + ciImg = self.videoTranserConvertor.convertVideo(asset: self.videoOriginalAsset, assetOutput: ao, type: self.selectedIndex, time: compositionTime) + break + default: + break + } + return ciImg + + } + + //加载assetReader和output + func loadVideoAssetReader() async { + do { + if self.assetTrack == nil { + assetTrack = try await self.videoOriginalAsset.loadTracks(withMediaType: .video).first! + } + + let timeRange = CMTimeRange(start: .zero, duration: .positiveInfinity) + DispatchQueue.main.async { + self.quickLoadAReader(timeRange: timeRange) + } + } catch { + print("Error loading video: \(error)") + } + } + + + func quickLoadAReader(timeRange:CMTimeRange) { +// self.lock.lock() + if(assetReader != nil){ + assetReader?.cancelReading() + } + assetReader = try! AVAssetReader(asset: self.videoOriginalAsset) + quickLoadAssetOutput() + assetReader!.timeRange = timeRange + assetReader!.add(assetOutput!) + assetReader!.startReading() +// self.lock.unlock() + } + + //在播放器的slider 拖动播放时调用 + func quickLoadAReaderWhenSeek(startCT:CMTime) { + let timeRange = CMTimeRange(start: startCT, duration: .positiveInfinity) + self.quickLoadAReader(timeRange: timeRange) + } + + //用于循环播放时调用,重置assetreader的timeRange + func quickLoadAReaderWhenReplayBack() { + let timeRange = CMTimeRange(start: .zero, duration: .positiveInfinity) + self.quickLoadAReader(timeRange: timeRange) + } + + func quickLoadAssetOutput() { + if(assetOutput != nil){ + assetOutput?.markConfigurationAsFinal() + assetOutput = nil + } + + assetOutput = AVAssetReaderTrackOutput( + track: self.assetTrack!, + outputSettings: [ + AVVideoDecompressionPropertiesKey: [ + kVTDecompressionPropertyKey_RequestedMVHEVCVideoLayerIDs: [0, 1] as CFArray, + ], + ] + ) +// assetOutput?.supportsRandomAccess = true +// assetOutput?.alwaysCopiesSampleData = false + } +} diff --git a/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/ZZHCustomSlider.swift b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/ZZHCustomSlider.swift index 243dcd0..a040a85 100644 --- a/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/ZZHCustomSlider.swift +++ b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/ZZHCustomSlider.swift @@ -15,10 +15,12 @@ class ZZHCustomSlider: UIView { var timeSlider:CustomSlider? var processChangeValueCallback:CustomSliderProcessChangeValueCallback? var preValue:Float = 0.0 //更新进度前的value - + var totalSec:Int = -1//代表视频总时长 let bgView:UIView = UIView() - + deinit { + print("zzhcustomslider....deinit...") + } override init(frame: CGRect) { super.init(frame: frame) @@ -83,12 +85,15 @@ class ZZHCustomSlider: UIView { if isMoving { var currentValue = max(0, min(value, 1)) //格式化为0.x - let formattedValue = String(format: "%.1f", currentValue) + let formattedValue = String(format: "%.2f", currentValue) currentValue = Float(formattedValue)! - + //保证相邻2次的值不同才有必要去触发进度的改变 if currentValue > preValue || currentValue < preValue { preValue = currentValue processChangeValueCallback(preValue,isMoving) + if self.totalSec > 0 {//已经获取到总时长的情况下再去更新起始位置的时间文本值 + self.sLabel?.text = ZZHHelper.formatVideoTime(second: Int(preValue*Float(self.totalSec))) + } } } else { @@ -98,9 +103,14 @@ class ZZHCustomSlider: UIView { } //外部播放时的更新当前滑块 - func exUpdateProcessValue(value:Float) { - self.timeSlider!.value = value - preValue = value + func exUpdateProcessValue(value:Float,currSec:Int,totalSec:Int) { + self.totalSec = totalSec + self.sLabel?.text = ZZHHelper.formatVideoTime(second: currSec) + self.eLabel?.text = ZZHHelper.formatVideoTime(second: totalSec) + let formattedValue = String(format: "%.1f", value) + let rv = Float(formattedValue)! + self.timeSlider!.setValue(value, animated: false) + preValue = rv } diff --git a/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/deapt/CCSpatialVideoDisplayController1.swift b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/deapt/CCSpatialVideoDisplayController1.swift new file mode 100644 index 0000000..695b851 --- /dev/null +++ b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/deapt/CCSpatialVideoDisplayController1.swift @@ -0,0 +1,684 @@ +// +// CCSpatialVideoDisplayController.swift +// SwiftProject +// +// Created by Zhang, Joyce on 2024/3/2. +// + +import UIKit +import AVFoundation +import Photos +import AVKit +import VideoToolbox +import Firebase +enum SpatialType : Int { + + /* + 单眼2D + 平行眼 + 红蓝立体 + 交叉眼 + + */ + + case monocular2D = 1 + case parallelEyes = 0 + case redBlueSolid = 2 + case crossedEyes = 3 + //以下两种标记:外部串流时使用 + case hsbs = 4 + case fsbs = 5 +} + +class CCSpatialVideoDisplayController: BaseController { + var externalDispalylink:CADisplayLink? + var externalVC:ZZHExternalViewController? + var isAirPlayActive:Bool = false { + didSet{ + //跟新selectedIndex、typeData的值 + if(isAirPlayActive){ + selectedIndex = .fsbs//在串流模式下,默认选项为hsbs + typeData = [(icon:"type_check",title:"3D FSBS",isHiden:false), + (icon:"type_check",title:"3D HSBS",isHiden:false) + ] + } + else{ + selectedIndex = .parallelEyes//在非串流模式下,默认选项为monocular2D + typeData = [ + (icon:"type_check",title:NSLocalizedString("平行眼", comment: ""),isHiden:false), + (icon:"type_check",title:NSLocalizedString("单眼2D", comment: ""),isHiden:false), + + + + (icon:"type_check",title:NSLocalizedString("红蓝立体", comment: ""),isHiden:false), + (icon:"type_check",title:NSLocalizedString("交叉眼", comment: ""),isHiden:false)] + } + //更新串流UI +// DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: { + self.setttinisScreenMirroring(isScreenMirroring: self.isAirPlayActive) +// }) + + } + + + } + + lazy var maskLabel:UILabel = { + let label = UILabel() + label.numberOfLines = 0 + label.textColor = .white + label.font = UIFont.systemFont(ofSize: 14) + label.textAlignment = .center + label.layer.cornerRadius = 6 + label.layer.masksToBounds = true + label.backgroundColor = UIColor.colorWithRGB(_r: 60.0, _g: 60.0, _b: 60.0, alpha: 0.1) + label.text = NSLocalizedString("外部串流中", comment: "") + label.isHidden = true + return label + }() + + var assetReader:AVAssetReader? + + var assetOutput:AVAssetReaderTrackOutput? + + var assetTrack:AVAssetTrack? + + let kNowTimeToUserDefaultKey_VideoDisplayController:String = "kNowTimeToUserDefaultKey_VideoDisplayController" + var link = false//是否已连接设备 + var isPlaying = false {//是否正在串流播放 + didSet { + maskLabel.isHidden = !isPlaying + } + } + + var selectedIndex:SpatialType = .parallelEyes//记录当前选择的菜单选项 + { + didSet{ + //进行相应解码操作,边解边播 + } + } + + + + let videoTranserConvertor = PlayByTransferConvertor() + + lazy var videoOriginalAsset:AVAsset = { + let asset = AVAsset(url: sourceVideoURL!) + return asset + }() + + var videoOriginalPHAsset:PHAsset? + + //图片源数据 + var sourceVideoURL:URL? + var imgData:Data? + + var player:AVPlayer? +// var needRestReaderTimeRange = false//标识是否需呀重置时间读取范围 + var playerController:AVPlayerViewController? + + lazy var mTopImgView:UIImageView = { + //393*236 +// let view = UIImageView(frame: CGRect(x: 0, y: 0, width: SCREEN_Width, height: SCREEN_Height * 236/393)) + let view = UIImageView(frame: CGRect(x: 0, y: 0, width: SCREEN_Width, height: 236)) + + view.image = UIImage.init(named: "BG_Top") + return view + }() + + lazy var transformButton: UIButton = { + + //76*56 + let transformButton = UIButton.init(type: UIButton.ButtonType.custom) + + transformButton.tag = 201 + transformButton.isSelected = false + transformButton.backgroundColor = UIColor(hexString: "#5326D6") + transformButton.addTarget(self, action: #selector(navgationButtonClick2(sender:)), for: UIControl.Event.touchUpInside) + let img2:UIImage = UIImage.init(named: "transform_button" as String)! + transformButton.setImage(img2, for: UIControl.State.normal) + transformButton.frame = CGRect(x: 0, y: 0, width: 56, height: 36) + transformButton.layer.cornerRadius = 18 + transformButton.layer.masksToBounds = true + transformButton.centerY = StatuBar_Height + NavBar_Height * 0.5 + transformButton.right = SCREEN_Width - 24 + + return transformButton + }() + + lazy var mTopCenterTypeButton: UIButton = { + //173*36 + let button = UIButton() + button.backgroundColor = UIColor.hexStringToColor(hexString: "#1F1E20") + button.tag = 202 + button.isSelected = false + button.addTarget(self, action: #selector(navgationButtonClick2(sender:)), for: UIControl.Event.touchUpInside) + button.frame = CGRect(x: 2, y: 10, width: SCREEN_Width * 0.4 + 20, height: 36) + button.centerY = StatuBar_Height + NavBar_Height * 0.5 + button.centerX = SCREEN_Width * 0.5 + button.clipsToBounds = true + button.layer.cornerRadius = 18 + button.layer.borderWidth = 1 + button.layer.borderColor = UIColor.white.cgColor + button.setTitle(NSLocalizedString("平行眼", comment: ""), for: UIControl.State.normal) + button.setImage(UIImage.init(named: "type_button_arrow_down"), for: .normal) + button.updateBtnEdgeInsets(style: .Right, space: 10) + button.setTitleColor(UIColor.white, for: UIControl.State.normal) + button.titleLabel?.font = KFont_Medium(14) + return button + }() + + + + var typeData:[(icon:String,title:String,isHiden:Bool)] = [(icon:"type_check",title:NSLocalizedString("平行眼", comment: ""),isHiden:false), + (icon:"type_check",title:NSLocalizedString("单眼2D", comment: ""),isHiden:false), + + (icon:"type_check",title:NSLocalizedString("红蓝立体", comment: ""),isHiden:false), + (icon:"type_check",title:NSLocalizedString("交叉眼", comment: ""),isHiden:false)] + { + didSet { + menuView.setData(datas: self.typeData) + } + } + + lazy var menuView: CCSpatialDisplayTypeView = { + //设置参数 + let parameters:[CCSpatialDisplayTypeConfigure] = [ + .PopMenuTextColor(UIColor.white), + .popMenuItemHeight(40), + .PopMenuTextFont(KFont_Medium(12)), + .PopMenuBackgroudColor(UIColor(hexString: "#1F1E20")) + ] + + + //init (test随机生成点位置,注意:arrow点是基于屏幕的位置) + let pointOnScreen = navtionImgView!.convert(CGPointMake(navtionImgView!.centerX, navtionImgView!.bottom), to: KWindow) + let popMenu = CCSpatialDisplayTypeView(menuWidth: SCREEN_Width * 0.4, arrow: pointOnScreen, datas: typeData,configures: parameters){[weak self] in + //dissmiss回调 + self?.mTopCenterTypeButton.setImage(UIImage.init(named: "type_button_arrow_down"), for: .normal) + } + return popMenu + }() + + lazy var progressView: UIProgressView = { + var view = UIProgressView (progressViewStyle:.default) + view.frame = CGRect(x: 24, y: SCREEN_Height - KStatusBarHeight - 20, width: SCREEN_Width-48, height: 36) + view.progress = 0.0 //默认进度50% + return view + }() + + + lazy var tipsButton: UIButton = { + + //76*56 + let button = UIButton.init(type: UIButton.ButtonType.custom) + button.tag = 203 + button.backgroundColor = .clear + button.addTarget(self, action: #selector(navgationButtonClick2(sender:)), for: UIControl.Event.touchUpInside) + let img2:UIImage = UIImage.init(named: NSLocalizedString("displayvc_desc", comment: ""))! + button.setImage(img2, for: UIControl.State.normal) + button.frame = CGRect(x: 0, y: 0, width: 173, height: 42) +// button.titleLabel?.font = KFont_Regular(14) +// button.titleLabel?.adjustsFontSizeToFitWidth = true +// button.updateBtnEdgeInsets(style: .Left, space: 8) + button.centerY = progressView.top - 70 + button.centerX = self.view.centerX + + return button + }() + + + //监听播放完毕 + @objc func notification_PlayerEndTime(notification:Notification){ + print("PlayerEndTime....") + player?.seek(to: .zero) + player?.play() +// +// self.playerController?.view.layer.sublayers?.forEach({ clayer in +// print("clayer.......\(clayer.self)") +// clayer.backgroundColor = UIColor.red.cgColor +// clayer.sublayers?.forEach({ sclayer in +// print("sclayer.......\(sclayer.self)") +// sclayer.backgroundColor = UIColor.blue.cgColor +// sclayer.sublayers?.forEach({ sscalyer in +// print("sscalyer.......\(sscalyer.self)") +// if let avPlayerLayer = sscalyer as? AVPlayerLayer { +// sscalyer.backgroundColor = UIColor.orange.cgColor +// sscalyer.sublayers?.forEach({ avSubLayer in +// print("avSubLayer.......\(avSubLayer.self)") +// avSubLayer.backgroundColor = UIColor.systemPink.cgColor +// if let avaPlayerLayer2 = avSubLayer as? AVPlayerLayer { +// avaPlayerLayer2.backgroundColor = UIColor.white.cgColor +// +// } +// }) +// } +// +// }) +// }) +// }) + + } + + //监听拖动播放的进度事件 +// @objc func notification_PlayerTimeJump(notification:Notification){ +//// needRestReaderTimeRange = true +// +// print("time jump....") +// +// } + + + override func viewDidLoad() { + super.viewDidLoad() + configureUI() + ZZHHelper.setNowTimeToUserDefaultWithKey(kNowTimeToUserDefaultKey_VideoDisplayController) + Task { + print("ccs viewdid load ---------") + await self.loadVideoAssetReader() + DispatchQueue.main.asyncAfter(deadline: .now()+0.01) {[weak self] in + self?.setUPPlayer() + // 检查当前是否已连接到 AirPlay 设备 + self?.checkAirPlayStatus() + + + } + } + } + + func setUPPlayer(){ + print("getPlayerItem ========") + player = AVPlayer() + player?.allowsExternalPlayback = true + player?.usesExternalPlaybackWhileExternalScreenIsActive = true + + print("getPlayerItem >>>>>>>") + + playerController = AVPlayerViewController() + playerController?.player = player + playerController!.view.backgroundColor = .clear + playerController?.view.frame = CGRect.init(x: 0, y: 170, width: self.view.frame.size.width, height: 400) + + self.addChild(playerController!) + self.view.addSubview(playerController!.view) + + self.maskLabel.frame = playerController!.view.frame + self.view.addSubview(self.maskLabel) + } + + + func configureUI(){ + self.view.backgroundColor = UIColor(hexString: "#060507") + + //设置返回按钮图片 + self.setLeftOneBtnImg(imgStr: "spatial_back_button") + self.setNavgationBarColorImg(color: .clear) + self.setNavgationBarLine(color: .clear) + + self.view.addSubview(mTopImgView) + self.view.bringSubviewToFront(self.navtionBar!) + + navtionBar?.addSubview(transformButton) + navtionBar?.addSubview(mTopCenterTypeButton) + + self.view.addSubview(tipsButton) + + + + + // 监听 AirPlay 设备的连接状态 + //添加外接屏幕链接通知 + NotificationCenter.default.addObserver(self, selector: #selector(exScreenWillConnectNotification(notification:)), name: UIScene.willConnectNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(exScreenDisConnectNotification(notification:)), name: UIScene.didDisconnectNotification, object: nil) + + + + + NotificationCenter.default.addObserver(self, selector: #selector(notification_PlayerEndTime(notification:)), name: AVPlayerItem.didPlayToEndTimeNotification, object: nil) +// NotificationCenter.default.addObserver(self, selector: #selector(notification_PlayerTimeJump(notification:)), name: AVPlayerItem.timeJumpedNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(notificatin_failedToPlayToEndTimeNotification(notifiation: )), name: AVPlayerItem.failedToPlayToEndTimeNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(notificatin_failedToPlayToEndTimeNotification(notifiation: )), name: AVPlayerItem.newErrorLogEntryNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(notification_didEnterBackgroundNotification(notification: )), name: UIApplication.didEnterBackgroundNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(notification_didActiveNotification(notification: )), name: UIApplication.didBecomeActiveNotification, object: nil) + } + + //屏幕进入后台 + @objc func notification_didEnterBackgroundNotification(notification:Notification) { + player?.pause() + + print("ccs .....notification_didEnterBackgroundNotification") + } + //屏幕进入前台 + @objc func notification_didActiveNotification(notification:Notification) { + self.checkAirPlayStatus() + print("ccs .....notification_didEnterBackgroundNotification") + } + + + + @objc func notificatin_failedToPlayToEndTimeNotification(notifiation:Notification) { + print("notificatin_failedToPlayToEndTimeNotification....") + + } + + @objc func notificatin_newErrorLogEntryNotification(notifiation:Notification) { + print("notificatin_newErrorLogEntryNotification....") + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + if externalVC != nil { + externalVC?.playerLayer?.player = nil + } + player?.pause() + releaseVideoComposition() + + let sec:TimeInterval = ZZHHelper.getSecFromUserDefaultByKey(kNowTimeToUserDefaultKey_VideoDisplayController) + Analytics.logEvent("preview_pv", parameters: ["refer_page":"视频预览页面","duration":sec]) + + print(".........ccs viewdid disappear....") + + } + + + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + if self.player?.currentItem == nil {//此处需要判断是否为空:1,在didload的时候会设置一次,在viewDidDisappear的时候会被置空 + let playerItem = self.getPlayerItem() + self.player?.replaceCurrentItem(with: playerItem) + } + + if self.externalVC != nil {//恢复外接屏幕的视频播放 + if self.externalVC?.playerLayer?.player == nil { + self.activeSession() + self.externalVC?.playerLayer?.player = self.player + } + } + self.player?.play() + } + + //MARK: - 监听设备投流 + @objc private func airPlayStatusDidChange(_ notification: Notification) { + checkAirPlayStatus() + if(isAirPlayActive) { + Analytics.logEvent("streaming_live", parameters: ["refer_page":"视频预览页面"]) + ZZHHelper.setNowTimeToUserDefaultWithKey("kNowTimeToUserDefaultKey_VideoDisplayController_airplay") + } + else { + let sec:TimeInterval = ZZHHelper.getSecFromUserDefaultByKey("kNowTimeToUserDefaultKey_VideoDisplayController_airplay") + Analytics.logEvent("streaming_live", parameters: ["refer_page":"视频预览页面","duration":sec]) + } + } + + @objc func exScreenWillConnectNotification(notification:Notification){ + print("exScreenWillConnectNotification>>>>>") + airPlayStatusDidChange( notification) + } + + @objc func exScreenDisConnectNotification(notification:Notification){ + print("exScreenDisConnectNotification>>>>>") + airPlayStatusDidChange( notification) + } + + + private func checkAirPlayStatus() { + print("设备连接变化") + + let currentRoute = AVAudioSession.sharedInstance().currentRoute + self.isAirPlayActive = currentRoute.outputs.contains { output in + return output.portType == AVAudioSession.Port.HDMI || + output.portType == AVAudioSession.Port.airPlay + } + } + + func setttinisScreenMirroring(isScreenMirroring:Bool){ + releaseVideoComposition() + + //已连接 + if(isScreenMirroring){ + self.dealTaskWhenExternalScreenConnect() + + + }else{ + //未连接 + self.dealTaskWhenExternalScreenDisConnect() + } + + + } + + //激活一下session + func activeSession(){ + do { + try AVAudioSession.sharedInstance().setActive(true) + try AVAudioSession.sharedInstance().setCategory(.playback,options: .allowAirPlay) + } + catch { + print("err: set audiosession:\(error)") + } + } + + //当外接屏幕连接时 + func dealTaskWhenExternalScreenConnect(){ +// if link == true { +// return +// } + + link = true + isPlaying = true + let playerItem = self.getPlayerItem() + player?.replaceCurrentItem(with: playerItem) + print("外接屏幕已连接.....") + activeSession() + + UIApplication.shared.connectedScenes.forEach { us in + print("uisence:\(us)\n") + let ws = us as! UIWindowScene + if ws.session.role.rawValue == "UIWindowSceneSessionRoleExternalDisplayNonInteractive"{ + externalVC = ws.windows.first?.rootViewController as? ZZHExternalViewController + if externalVC != nil { + if let ed = externalDispalylink { + ed.invalidate() + } + externalDispalylink = ws.windows.first?.screen.displayLink(withTarget: self, selector: #selector(displayUpdate(caDisplayLink:))) + externalDispalylink?.add(to: RunLoop.main, forMode: RunLoop.Mode.common) + } + } + } + + updateTopCenterButtonWhenIsPlayingChange() + + } + + //当处理外部设备连接状态时,串流状态更新 + func updateTopCenterButtonWhenIsPlayingChange() { + //串流播放中 + mTopCenterTypeButton.setImage(UIImage.init(named: "linked_button"), for: .normal) + mTopCenterTypeButton.setTitleColor(UIColor(hexString: "#D0C0FF"), for: .normal) + mTopCenterTypeButton.layer.borderColor = UIColor(hexString: "#D0C0FF").cgColor + mTopCenterTypeButton.backgroundColor = UIColor(hexString: "#5326D6") + mTopCenterTypeButton.updateBtnEdgeInsets(style: .Left, space: 5) + + + //展示弹出列表尾部 + if isPlaying { + //正在串流中 --- 按钮显示为【结束串流】 + menuView.showFooterView(isShow: true, showText: NSLocalizedString("结束串流", comment: "")) + mTopCenterTypeButton.setTitle(NSLocalizedString("外部串流中", comment: ""), for: UIControl.State.normal) + }else{ + //未串流 --- 按钮显示为【开始串流】 + menuView.showFooterView(isShow: true, showText: NSLocalizedString("开始串流", comment: "")) + mTopCenterTypeButton.setTitle(NSLocalizedString("已连接外部设备", comment: ""), for: UIControl.State.normal) + } + } + + //当外接屏幕断开时 + func dealTaskWhenExternalScreenDisConnect(){ + print("外接屏幕已断开.....") + let playerItem = self.getPlayerItem() + player?.replaceCurrentItem(with: playerItem) + + link = false + isPlaying = false + // 当前未连接到 AirPlay 设备 + if self.selectedIndex == .monocular2D { + mTopCenterTypeButton.setTitle(NSLocalizedString("单眼2D", comment: ""), for: UIControl.State.normal) + }else if self.selectedIndex == .redBlueSolid { + mTopCenterTypeButton.setTitle(NSLocalizedString("红蓝立体", comment: ""), for: UIControl.State.normal) + }else if self.selectedIndex == .crossedEyes { + mTopCenterTypeButton.setTitle(NSLocalizedString("交叉眼", comment: ""), for: UIControl.State.normal) + } + else if self.selectedIndex == .parallelEyes { + mTopCenterTypeButton.setTitle(NSLocalizedString("平行眼", comment: ""), for: UIControl.State.normal) + } + + mTopCenterTypeButton.setImage(UIImage.init(named: "type_button_arrow_down"), for: .normal) + mTopCenterTypeButton.setTitleColor(UIColor.white, for: UIControl.State.normal) + mTopCenterTypeButton.layer.borderColor = UIColor.white.cgColor + mTopCenterTypeButton.backgroundColor = UIColor(hexString: "#1F1E20") + mTopCenterTypeButton.updateBtnEdgeInsets(style: .Right, space: 10) + + //隐藏弹出列表尾部 + menuView.showFooterView(isShow: false, showText: "") + externalVC?.playerLayer?.player = nil + self.playerController?.player = player + player?.play() + externalVC = nil + } + + @objc func displayUpdate(caDisplayLink:CADisplayLink) { + if let has_exvc = externalVC { + if has_exvc.playerLayer?.player == nil { + has_exvc.imageView?.isHidden = true + has_exvc.playerLayer?.isHidden = false + self.playerController?.player = nil + has_exvc.playerLayer?.player = player + player?.play() + externalDispalylink?.invalidate() + externalDispalylink = nil + } + } + + } + + + deinit { + print("patialvideodisplaycontroler deinit......") + releaseVideoComposition() + assetReader?.cancelReading() + NotificationCenter.default.removeObserver(self) +// player?.removeObserver(self, forKeyPath: "status") + } + + func releaseVideoComposition() { + if externalDispalylink != nil { + externalDispalylink?.invalidate() + externalDispalylink = nil + } + self.player?.currentItem?.videoComposition = nil + self.player?.replaceCurrentItem(with: nil) + } + + + //MARK: - action + @objc public func navgationButtonClick2(sender:UIButton){ + + if sender.tag == 200 { + //左边按钮 + }else if sender.tag == 201 { + //右边按钮 + let transVC = VRVideoTransformController() + transVC.videoOriginalPHAsset = self.videoOriginalPHAsset + transVC.sourceVideoURL = sourceVideoURL + self.navigationController?.pushViewController(transVC, animated: true) + Analytics.logEvent("preview_trans_click", parameters: nil) + }else if sender.tag == 202 { + //中间按钮 + + menuView.show() + if link { + //展示弹出列表尾部 + if isPlaying { + //正在串流中 --- 按钮显示为【结束串流】 + menuView.showFooterView(isShow: true, showText: NSLocalizedString("结束串流", comment: "")) + mTopCenterTypeButton.setTitle(NSLocalizedString("外部串流中", comment: ""), for: UIControl.State.normal) + }else{ + //未串流 --- 按钮显示为【开始串流】 + menuView.showFooterView(isShow: true, showText: NSLocalizedString("开始串流", comment: "")) + mTopCenterTypeButton.setTitle(NSLocalizedString("已连接外部设备", comment: ""), for: UIControl.State.normal) + } + }else{ + mTopCenterTypeButton.setImage(UIImage.init(named: "type_button_arrow_up"), for: .normal) + } + + + //顶部中间的按钮选中某个选项后的回调click + menuView.didSelectMenuBlock = { [weak self](index:Int)->Void in + print("block select \(index)") + self?.selectedSpatialType(selectedIndex: index) + + let title = self?.typeData[index].title + self?.mTopCenterTypeButton.setTitle(title, for: UIControl.State.normal) + } + + //开始、结束串流的回调 + menuView.tapFooterActionBlock = {[weak self] in + self?.startOrEndExternalVR() + } + + }else if sender.tag == 203 { + //tips + let view = CCDeviceOperationListView.init(frame: CGRectMake(0, 0, KScreenWidth, KScreenHeight)) + KWindow?.addSubview(view) + } + } + + func startOrEndExternalVR() { + if(link == true){ + if isPlaying {//结束串流 + isPlaying = false + if self.externalVC != nil { + externalVC?.playerLayer?.player = nil + player?.pause() + releaseVideoComposition() + DispatchQueue.main.asyncAfter(deadline: .now()+0.15, execute: {[weak self] in + let playerItem = self?.getPlayerItem() + self?.player?.replaceCurrentItem(with: playerItem) + self?.playerController?.player = self?.player + self?.player?.play() + }) + + } + } + else {//开始串流 + if self.externalVC != nil { + self.playerController?.player = nil + player?.pause() + releaseVideoComposition() + let playerItem = self.getPlayerItem() + player?.replaceCurrentItem(with: playerItem) + externalVC?.playerLayer?.player = player + player?.play() + isPlaying = true + } + } + + } + + + updateTopCenterButtonWhenIsPlayingChange() + } + + func selectedSpatialType(selectedIndex:Int){ + var si = selectedIndex + if self.typeData.count == 2 { + si = selectedIndex + 4 + } + let rsi : SpatialType = SpatialType(rawValue: si) ?? .monocular2D + self.selectedIndex = rsi + +} + +} diff --git a/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/CCSpatialVideoDisplayForVideoTask.swift b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/deapt/CCSpatialVideoDisplayForVideoTask1.swift similarity index 99% rename from SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/CCSpatialVideoDisplayForVideoTask.swift rename to SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/deapt/CCSpatialVideoDisplayForVideoTask1.swift index 8b20b6d..9a5e42f 100644 --- a/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/CCSpatialVideoDisplayForVideoTask.swift +++ b/SwiftProject/SwiftProject/Project/Controller/RecordingVideo/CCSpatialVideoDisplayController/deapt/CCSpatialVideoDisplayForVideoTask1.swift @@ -154,9 +154,9 @@ extension CCSpatialVideoDisplayController { assetReader!.startReading() //加载完毕之后再进行play // DispatchQueue.main.async { -// +// // self.player?.play() -// +// // print("资产加载完毕。。。。开始播放player\n err:\(self.player?.error)") // } } diff --git a/tdvideo/tdvideo.xcodeproj/project.xcworkspace/xcuserdata/aaa.xcuserdatad/UserInterfaceState.xcuserstate b/tdvideo/tdvideo.xcodeproj/project.xcworkspace/xcuserdata/aaa.xcuserdatad/UserInterfaceState.xcuserstate index c0137b3..650738d 100644 Binary files a/tdvideo/tdvideo.xcodeproj/project.xcworkspace/xcuserdata/aaa.xcuserdatad/UserInterfaceState.xcuserstate and b/tdvideo/tdvideo.xcodeproj/project.xcworkspace/xcuserdata/aaa.xcuserdatad/UserInterfaceState.xcuserstate differ