完美解决播放卡顿问题

This commit is contained in:
Zhihai Zhu 2024-04-20 11:31:53 +08:00
parent 0270796016
commit 1eb4326d61
8 changed files with 233 additions and 76 deletions

View File

@ -12,102 +12,202 @@
<key>DeviceKit.xcscheme_^#shared#^_</key> <key>DeviceKit.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>13</integer> <integer>2</integer>
</dict>
<key>FBSDKCoreKit.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>31</integer>
</dict>
<key>FacebookCore.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>21</integer>
</dict>
<key>Firebase.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>15</integer>
</dict>
<key>FirebaseABTesting-FirebaseABTesting_Privacy.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>26</integer>
</dict>
<key>FirebaseABTesting.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>16</integer>
</dict>
<key>FirebaseAnalytics.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>33</integer>
</dict>
<key>FirebaseCore-FirebaseCore_Privacy.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>11</integer>
</dict> </dict>
<key>FirebaseCore.xcscheme_^#shared#^_</key> <key>FirebaseCore.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>0</integer> <integer>29</integer>
</dict> </dict>
<key>FirebaseCoreExtension.xcscheme_^#shared#^_</key> <key>FirebaseCoreExtension.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>12</integer> <integer>12</integer>
</dict> </dict>
<key>FirebaseCoreInternal-FirebaseCoreInternal_Privacy.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>27</integer>
</dict>
<key>FirebaseCoreInternal.xcscheme_^#shared#^_</key> <key>FirebaseCoreInternal.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>6</integer> <integer>14</integer>
</dict> </dict>
<key>FirebaseCrashlytics.xcscheme_^#shared#^_</key> <key>FirebaseCrashlytics.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>2</integer> <integer>2</integer>
</dict> </dict>
<key>FirebaseInstallations-FirebaseInstallations_Privacy.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>12</integer>
</dict>
<key>FirebaseInstallations.xcscheme_^#shared#^_</key> <key>FirebaseInstallations.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>8</integer> <integer>20</integer>
</dict> </dict>
<key>FirebaseMessaging.xcscheme_^#shared#^_</key> <key>FirebaseMessaging.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>19</integer> <integer>19</integer>
</dict> </dict>
<key>FirebaseRemoteConfig-FirebaseRemoteConfig_Privacy.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>6</integer>
</dict>
<key>FirebaseRemoteConfig.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>10</integer>
</dict>
<key>FirebaseRemoteConfigInterop.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>7</integer>
</dict>
<key>FirebaseSessions.xcscheme_^#shared#^_</key> <key>FirebaseSessions.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>15</integer> <integer>15</integer>
</dict> </dict>
<key>FirebaseSharedSwift.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>9</integer>
</dict>
<key>GoogleAppMeasurement.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>34</integer>
</dict>
<key>GoogleDataTransport.xcscheme_^#shared#^_</key> <key>GoogleDataTransport.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>9</integer> <integer>9</integer>
</dict> </dict>
<key>GoogleUtilities-GoogleUtilities_Privacy.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>28</integer>
</dict>
<key>GoogleUtilities.xcscheme_^#shared#^_</key> <key>GoogleUtilities.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>16</integer> <integer>4</integer>
</dict>
<key>KeychainAccess.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>17</integer>
</dict> </dict>
<key>Kingfisher-Kingfisher.xcscheme_^#shared#^_</key> <key>Kingfisher-Kingfisher.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>18</integer> <integer>32</integer>
</dict> </dict>
<key>Kingfisher.xcscheme_^#shared#^_</key> <key>Kingfisher.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>10</integer> <integer>24</integer>
</dict> </dict>
<key>LLCycleScrollView.xcscheme_^#shared#^_</key> <key>LLCycleScrollView.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>21</integer> <integer>25</integer>
</dict> </dict>
<key>Pods-SwiftProject.xcscheme_^#shared#^_</key> <key>Pods-SwiftProject.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>20</integer> <integer>3</integer>
</dict>
<key>PromisesObjC-FBLPromises_Privacy.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>30</integer>
</dict> </dict>
<key>PromisesObjC.xcscheme_^#shared#^_</key> <key>PromisesObjC.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>5</integer> <integer>19</integer>
</dict> </dict>
<key>PromisesSwift.xcscheme_^#shared#^_</key> <key>PromisesSwift.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>17</integer> <integer>17</integer>
</dict> </dict>
<key>SVProgressHUD.xcscheme_^#shared#^_</key> <key>SDWebImage-SDWebImage.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>3</integer> <integer>23</integer>
</dict> </dict>
<key>SnapKit.xcscheme_^#shared#^_</key> <key>SDWebImage.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>1</integer> <integer>1</integer>
</dict> </dict>
<key>SVProgressHUD.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>18</integer>
</dict>
<key>SnapKit.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>13</integer>
</dict>
<key>TZImagePickerController.xcscheme_^#shared#^_</key> <key>TZImagePickerController.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>7</integer> <integer>8</integer>
</dict>
<key>nanopb-nanopb_Privacy.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>22</integer>
</dict> </dict>
<key>nanopb.xcscheme_^#shared#^_</key> <key>nanopb.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>11</integer> <integer>5</integer>
</dict> </dict>
</dict> </dict>
</dict> </dict>

View File

@ -7,7 +7,7 @@
<key>SwiftProject.xcscheme_^#shared#^_</key> <key>SwiftProject.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>4</integer> <integer>0</integer>
</dict> </dict>
</dict> </dict>
</dict> </dict>

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>Promises (Playground) 1.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>36</integer>
</dict>
<key>Promises (Playground) 2.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>37</integer>
</dict>
<key>Promises (Playground).xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>35</integer>
</dict>
</dict>
</dict>
</plist>

View File

@ -11,7 +11,8 @@ import AVKit
//assetoutput 线 //assetoutput 线
//let AssetGlobalQueue = DispatchQueue.global(qos: DispatchQoS.QoSClass.default) //let AssetGlobalQueue = DispatchQueue.global(qos: DispatchQoS.QoSClass.default)
let CustomPlayerShareSemaphore = DispatchSemaphore(value: 1) //let CustomPlayerShareSemaphore = DispatchSemaphore(value: 1)
//let CustomPlayerShareRecursiveLock = NSRecursiveLock()
class ZZHCustomPlayer: UIView { class ZZHCustomPlayer: UIView {
@ -41,6 +42,8 @@ class ZZHCustomPlayer: UIView {
} }
} }
var playerIsSeeking:Bool = false//
//ui //ui
var playerLayerBgView:UIView? var playerLayerBgView:UIView?
@ -137,12 +140,13 @@ class ZZHCustomPlayer: UIView {
//sliderseek //sliderseek
func manualToSeekPlay(value:Float,isMoving:Bool){ func manualToSeekPlay(value:Float,isMoving:Bool){
if isMoving { if isMoving {
self.play(false) self.play(false)
print("此处已暂停播放,那么后面希望不要出现 sta.....> 的打印信息") print("此处已暂停播放,那么后面希望不要出现 sta.....> 的打印信息")
} }
Task { // Task {
let totalSec = CMTimeGetSeconds((self.avPlayer?.currentItem?.duration)!) let totalSec = CMTimeGetSeconds((self.avPlayer?.currentItem?.duration)!)
var atSec = Float(totalSec) * value var atSec = Float(totalSec) * value
var timeScale:CMTimeScale? = self.avPlayer?.currentItem?.duration.timescale var timeScale:CMTimeScale? = self.avPlayer?.currentItem?.duration.timescale
@ -154,24 +158,30 @@ class ZZHCustomPlayer: UIView {
} }
let ct = CMTime(value:CMTimeValue(atSec) , timescale: timeScale!) let ct = CMTime(value:CMTimeValue(atSec) , timescale: timeScale!)
// CustomPlayerShareSemaphore.wait() // CustomPlayerShareSemaphore.wait()
// CustomPlayerShareRecursiveLock.lock()
// self.quickLoadAReaderWhenSeek(startCT: ct) // self.quickLoadAReaderWhenSeek(startCT: ct)
// CustomPlayerShareRecursiveLock.unlock()
// CustomPlayerShareSemaphore.signal() // CustomPlayerShareSemaphore.signal()
print("正在seek.....\(ct) ismainthread:\(Thread.isMainThread)") print("正在seek.....\(ct) ismainthread:\(Thread.isMainThread)")
DispatchQueue.main.async { // DispatchQueue.main.async {
playerIsSeeking = true
self.avPlayer?.seek(to: ct,toleranceBefore:.zero,toleranceAfter: .zero, completionHandler: { finished in self.avPlayer?.seek(to: ct,toleranceBefore:.zero,toleranceAfter: .zero, completionHandler: { finished in
if finished { if finished {
if(!isMoving) {//, if(!isMoving) {//,
CustomPlayerShareSemaphore.wait() // CustomPlayerShareSemaphore.wait()
self.quickLoadAReaderWhenSeek(startCT: ct) // CustomPlayerShareRecursiveLock.lock()
CustomPlayerShareSemaphore.signal() // self.quickLoadAReaderWhenSeek(startCT: ct)
// CustomPlayerShareRecursiveLock.unlock()
// CustomPlayerShareSemaphore.signal()
self.play(true) self.play(true)
self.playerIsSeeking = false
} }
} }
}) })
} // }
} // }

View File

@ -13,51 +13,51 @@ import VideoToolbox
extension ZZHCustomPlayer { 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
// }
// weakSelf.convertFrame(request:request)
//
// } completionHandler: { ac, err in
// if err != nil {
// print("coposition\(err)")
// }
// else{
// print("composition ok....")
// temItem.videoComposition = ac
// }
// }
// return temItem
// }
func getPlayerItem() -> AVPlayerItem { func getPlayerItem() -> AVPlayerItem {
let temItem = AVPlayerItem(asset: self.videoOriginalAsset) let temItem = AVPlayerItem(asset: self.videoOriginalAsset)
let timeRange = CMTimeRange(start: CMTime.zero, duration: temItem.asset.duration) AVVideoComposition.videoComposition(with: temItem.asset) { [weak self] request in
let videoTracks = temItem.asset.tracks(withMediaType: AVMediaType.video)
guard let sourceVideoTrack = videoTracks.first else {
return temItem
}
let videoComposition = AVMutableVideoComposition(propertiesOf: temItem.asset) // print("frame....")
videoComposition.customVideoCompositorClass = ZZHCustomVideoCompositor.self guard let weakSelf = self else {
let instruction = ZZHCustomVideoCompositionInstruction(track: sourceVideoTrack, timeRange: timeRange, transform: sourceVideoTrack.preferredTransform, targetSize: sourceVideoTrack.naturalSize,sourceVideoURL: self.sourceVideoURL!) print("self 被销毁了.....")
custominstruction = instruction return
videoComposition.instructions = [instruction] }
temItem.videoComposition = videoComposition weakSelf.convertFrame(request:request)
} completionHandler: { ac, err in
if err != nil {
print("初始化coposition报错\(err)")
}
else{
print("composition 生成ok....")
temItem.videoComposition = ac
}
}
return temItem return temItem
} }
// func getPlayerItem() -> AVPlayerItem {
// let temItem = AVPlayerItem(asset: self.videoOriginalAsset)
// let timeRange = CMTimeRange(start: CMTime.zero, duration: temItem.asset.duration)
// let videoTracks = temItem.asset.tracks(withMediaType: AVMediaType.video)
// guard let sourceVideoTrack = videoTracks.first else {
// return temItem
// }
//
// let videoComposition = AVMutableVideoComposition(propertiesOf: temItem.asset)
// videoComposition.customVideoCompositorClass = ZZHCustomVideoCompositor.self
// let instruction = ZZHCustomVideoCompositionInstruction(track: sourceVideoTrack, timeRange: timeRange, transform: sourceVideoTrack.preferredTransform, targetSize: sourceVideoTrack.naturalSize,sourceVideoURL: self.sourceVideoURL!)
// custominstruction = instruction
// videoComposition.instructions = [instruction]
// temItem.videoComposition = videoComposition
// return temItem
// }
func convertFrame(request:AVAsynchronousCIImageFilteringRequest){ func convertFrame(request:AVAsynchronousCIImageFilteringRequest){
let compositionTime = request.compositionTime let compositionTime = request.compositionTime
let end:CMTime = CMTimeMake(value: Int64(compositionTime.value+1), timescale: compositionTime.timescale) let end:CMTime = CMTimeMake(value: Int64(compositionTime.value+1), timescale: compositionTime.timescale)
let tr = CMTimeRange(start: compositionTime, end: end) // let tr = CMTimeRange(start: compositionTime, end: end)
var ciImg:CIImage? = nil var ciImg:CIImage? = nil
switch self.selectedIndex { switch self.selectedIndex {
@ -66,19 +66,31 @@ extension ZZHCustomPlayer {
break break
default : default :
ciImg = self.otherModeImgWithMode(mode: self.selectedIndex,tr:tr,compositionTime: compositionTime) if self.playerIsSeeking {
quickLoadAReaderWhenSeek(startCT: compositionTime)
ciImg = self.otherModeImgWithMode(mode: self.selectedIndex,compositionTime: compositionTime)
}
else {
ciImg = self.otherModeImgWithMode(mode: self.selectedIndex,compositionTime: compositionTime)
guard let ciImg else {print("未合成成功.....,此处将进行重新构建assetoutput,然后再次重拾")
quickLoadAReaderWhenSeek(startCT: compositionTime)
ciImg = self.otherModeImgWithMode(mode: self.selectedIndex,compositionTime: compositionTime)
break
}
}
break break
} }
if let ciImg { if let ciImg {
request.finish(with: ciImg, context: nil) request.finish(with: ciImg, context: nil)
} }
else { else {
print("未合成成功.....") print("未合成成功.....,")
request.finish(with: request.sourceImage, context: nil) request.finish(with: request.sourceImage, context: nil)
} }
} }
func otherModeImgWithMode(mode:SpatialType,tr:CMTimeRange,compositionTime:CMTime)->CIImage? { func otherModeImgWithMode(mode:SpatialType,compositionTime:CMTime)->CIImage? {
guard let ao = self.assetOutput else { guard let ao = self.assetOutput else {
print("assetOutput 应该是没有被创建成功.....") print("assetOutput 应该是没有被创建成功.....")
return nil return nil
@ -168,11 +180,11 @@ extension ZZHCustomPlayer {
func quickLoadAssetOutput() { func quickLoadAssetOutput() {
if(assetOutput != nil){ if(assetOutput != nil){
print("正在释放assetoutput----assetOutput:\(assetOutput) \n\(Date.now.timeIntervalSince1970)") // print("assetoutput----assetOutput:\(assetOutput) \n\(Date.now.timeIntervalSince1970)")
assetOutput?.markConfigurationAsFinal() assetOutput?.markConfigurationAsFinal()
print("正在释放assetoutput....assetOutput:\(assetOutput) \n\(Date.now.timeIntervalSince1970)") // print("assetoutput....assetOutput:\(assetOutput) \n\(Date.now.timeIntervalSince1970)")
assetOutput = nil assetOutput = nil
print("释放完毕assetoutput....thread:\(Thread.current) \n\(Date.now.timeIntervalSince1970)") // print("assetoutput....thread:\(Thread.current) \n\(Date.now.timeIntervalSince1970)")
} }
assetOutput = AVAssetReaderTrackOutput( assetOutput = AVAssetReaderTrackOutput(

View File

@ -55,7 +55,7 @@ final class ZZHCustomVideoCompositor: NSObject, AVVideoCompositing {
// //
self.activeRequests.append(request) self.activeRequests.append(request)
print("startRequest ....AVAsynchronousVideoCompositionRequest: \(self.cancelled) request:\(request)") // print("startRequest ....AVAsynchronousVideoCompositionRequest: \(self.cancelled) request:\(request)")
guard !self.cancelled else { guard !self.cancelled else {
print("startRequest cancell....") print("startRequest cancell....")
request.finishCancelledRequest() request.finishCancelledRequest()
@ -64,18 +64,20 @@ final class ZZHCustomVideoCompositor: NSObject, AVVideoCompositing {
return return
} }
print("startRequest queue:\(self.queue) current:\(Thread.current)") // print("startRequest queue:\(self.queue) current:\(Thread.current)")
CustomPlayerShareSemaphore.wait() // CustomPlayerShareSemaphore.wait()
// CustomPlayerShareRecursiveLock.lock()
guard let renderedBuffer = self.renderFrame(forRequest: request) else { guard let renderedBuffer = self.renderFrame(forRequest: request) else {
request.finish(with: ZZHCustomVideoCompositoringError.invalidRequest) request.finish(with: ZZHCustomVideoCompositoringError.invalidRequest)
print("报了个异常,但是应该执行不到....") print("报了个异常,但是应该执行不到....")
return return
} }
CustomPlayerShareSemaphore.signal() // CustomPlayerShareSemaphore.signal()
// CustomPlayerShareRecursiveLock.unlock()
request.finish(withComposedVideoFrame: renderedBuffer) request.finish(withComposedVideoFrame: renderedBuffer)
// //
self.activeRequests.removeAll { $0 === request } self.activeRequests.removeAll { $0 === request }
print("完成执行 finish.....") // print(" finish.....")
} }
} }

View File

@ -17,15 +17,15 @@ class PlayByTransferConvertor {
func convertVideo(asset:AVAsset, assetOutput:AVAssetReaderTrackOutput,type:SpatialType,time: CMTime)->(CIImage?,CMTime?) { func convertVideo(asset:AVAsset, assetOutput:AVAssetReaderTrackOutput,type:SpatialType,time: CMTime)->(CIImage?,CMTime?) {
var newpb:CIImage? = nil var newpb:CIImage? = nil
var presentationTime:CMTime? = nil var presentationTime:CMTime? = nil
print("sta.....>>>>>>>thread:\(Thread.current) assetOutput:\(assetOutput) \n\(Date.now.timeIntervalSince1970)") print("sta.....>>>>>>>thread")
while let nextSampleBuffer = assetOutput.copyNextSampleBuffer() { while let nextSampleBuffer = assetOutput.copyNextSampleBuffer() {
presentationTime = CMSampleBufferGetPresentationTimeStamp(nextSampleBuffer) presentationTime = CMSampleBufferGetPresentationTimeStamp(nextSampleBuffer)
print("presentationTime: \(presentationTime) \ntime: \(time)") print("presentationTime: \(presentationTime) \ntime: \(time)")
if presentationTime! > time {//buffer>time, // if presentationTime! > time {//buffer>time,
print("如果当前获取的buffer的时间>time的时间,则直接返回即可...") // print("buffer>time,...")
break // break
} // }
if presentationTime == time { if presentationTime == time {
guard let taggedBuffers = nextSampleBuffer.taggedBuffers else { break } guard let taggedBuffers = nextSampleBuffer.taggedBuffers else { break }
@ -80,8 +80,11 @@ class PlayByTransferConvertor {
CMSampleBufferInvalidate(nextSampleBuffer) CMSampleBufferInvalidate(nextSampleBuffer)
break break
} }
else{//,break,time,
break
}
} }
print("PlayByTransferConvertor 测试看是否有返回....") // print("PlayByTransferConvertor ....")
return (newpb,presentationTime) return (newpb,presentationTime)
} }