2 Replies
      Latest reply on Dec 2, 2019 2:06 PM by dhoerl
      dhoerl Level 1 Level 1 (0 points)

        I have a 30 second video I want to loop and uses an AVVideoComposition. It works flawlessly in the Catalina Simulator, but fails on my iPad 9.7". Originally I thought the problem was with the CIFilters, but in the end it was the simple existence of the composition block:


        func runMovie() {
            playerItem = AVPlayerItem(asset: asset)
            let videoComposition = AVVideoComposition(asset: asset, applyingCIFiltersWithHandler: { request in
                let outputImage = request.sourceImage
                request.finish(with: outputImage, context: nil)
            playerItem.videoComposition = videoComposition
            let duration = CMTime(seconds: 30.0, preferredTimescale: CMTimeScale(1))
            let plyr = AVQueuePlayer()
            let timeRange = CMTimeRange(start: CMTime.zero, duration: duration)
            playerLooper = AVPlayerLooper(player: plyr, templateItem: playerItem, timeRange: timeRange)
            playerLayer = AVPlayerLayer(player: player)
            playerLayer.frame = view.bounds //bounds of the view in which AVPlayer should be displayed
            playerLayer.videoGravity = .resizeAspect
            print("PLAY VIDEO", player.status.rawValue)
            startDate = Date()
            let _ = timer// starts 1 second timer


        The player runs one or more loops, then the video stops. I have a timer running checking the AVLooper status, and this is the error I see:


        //if playerLooper.status == .failed {
        //  print("ERROR:", playerLooper.error ?? "No Error")
        //  ERROR: Error Domain=AVFoundationErrorDomain Code=-11819 "Cannot Complete Action" UserInfo={NSLocalizedDescription=Cannot Complete Action, NSLocalizedRecoverySuggestion=Try again later.}
        Leave the video composition out, it loops forever, add the compostion, it fails in a min or two.


        PS: this is a HEVC video I created from a standard mov per the 2019 WWDC 506 Session:


        guard let export = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHEVCHighestQualityWithAlpha) else { fatalError() }
        let scale = CMTimeScale(1)
        export.timeRange = CMTimeRange(start: CMTime.zero, duration: CMTime(seconds: 31.0, preferredTimescale: scale))
        export.outputFileType = AVFileType.mov
        //let url = URL(fileURLWithPath: "/tmp/Original-transparent.mov")
        let url = URL(fileURLWithPath: "/tmp/Original-transparent.mov")
        try? FileManager.default.removeItem(at: url)
        export.outputURL = url
        export.videoComposition = videoComposition
        export.exportAsynchronously(completionHandler: {
            DispatchQueue.main.async {
                NSLog("...VIDEO DONE")
                self.makingVideoNow = false
        NSLog("START VIDEO...")
        • Re: AVPlayerLooper fails when there is a AVVideoComposition
          dhoerl Level 1 Level 1 (0 points)

          Now I'm really pulling my hair out. I added this test to be 100% sure the video was properly constructed:


          let ret = videoComposition.isValid(for: asset, timeRange: timeRange, validationDelegate: self)
          print("VALIDATE", ret)


          All possible delegate calls implemented with print statemens - but it returns true and I got no delegate calls. But now, the looper is not failing! Sixteen loops so far and still going strong (will leave it on!). Grrrrr

            • Re: AVPlayerLooper fails when there is a AVVideoComposition
              dhoerl Level 1 Level 1 (0 points)

              OK - never fails now if there is no filtering?!?! But if I add a filter (either of the two), then it fails at the next loop:


                  let videoComposition = AVVideoComposition(asset: asset, applyingCIFiltersWithHandler: { request in
                      var outputImage = request.sourceImage
                      defer {
                          request.finish(with: outputImage, context: nil)
                      do {
                          var angle = self.hueAngle
                          let radians = degrees2radians(angle)
                          angle = (angle + 1) % 360
                          self.hueAngle = angle
                          self.hueFilter.angle = radians
                          self.hueFilter.inputImage = outputImage
                          outputImage = self.hueFilter.outputImage!
                      do {
                          var radius = self.radius
                          let inc = self.inc
                          radius += inc
                          if radius > 5 {
                              radius = 5
                              self.inc *= -1
                          if radius < 0 {
                              radius = 0
                              self.inc *= -1
                          self.radius = radius
                          self.blurFilter.radius = radius
                          self.blurFilter.inputImage = outputImage
                          outputImage = self.blurFilter.outputImage!


              Obviously validating the composition makes something better in the composition object.