0

Updated code sample, console output and information

I'm trying to get the total duration of a video composition, divide it by 10, round up, then loop through and splice the video composition at those intervals.

It works, but after the 'currentDuration' becomes 60+, it throws a "The requested URL was not found on this server."

Basically if it determines it needs to produce 9 clips, it succeeds the first 6 and fails the other 3. Also my numLoop isn't working as expected, it almost seems the while loop finishes before any of the 9 clips are attempted.

Would love some help/insight into getting all 9 clips exporting.

I've noticed if the video is less than 60 seconds long, it has 100% success rate. Any video I choose over 60 seconds will fails on the 7th clip.

Here's my method:

func splitVideo(videoComposition: AVMutableVideoComposition) {


let fileManager = NSFileManager.defaultManager()
let documentsPath : String = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.UserDomainMask,true)[0]

//grab total duration/number of splits
let exporter: AVAssetExportSession = AVAssetExportSession(asset: asset!, presetName:AVAssetExportPresetHighestQuality)!
let totalDuration = Float64(CMTimeGetSeconds(exporter.asset.duration))
let totalDurationPieces = (totalDuration/10)
var numberOfSplits=Int(ceil(Double(totalDurationPieces)))

//prepare for loop
var loopNum : Int = 0
var currentDuration : Float64 = 0
var sessionNumber = (arc4random()%1000)

let opQueue = NSOperationQueue()
    opQueue.maxConcurrentOperationCount = 1

    while loopNum < numberOfSplits {

        //new exporter
        var destinationPath: String = documentsPath + "/splitVideo-"+String(sessionNumber)
        destinationPath+="-"+String(Int(loopNum))+".mp4"

        let new_exporter = AVAssetExportSession(asset: asset!, presetName:AVAssetExportPresetHighestQuality)!

        new_exporter.outputURL = NSURL(fileURLWithPath: destinationPath as String)
        new_exporter.videoComposition = videoComposition
        new_exporter.outputFileType = AVFileTypeMPEG4
        new_exporter.shouldOptimizeForNetworkUse = false

        new_exporter.timeRange = CMTimeRangeMake(
            CMTimeMakeWithSeconds(currentDuration, framesPerSecond!),CMTimeMakeWithSeconds(Float64(10),framesPerSecond!))

        // Set up the exporter, then:
        opQueue.addOperationWithBlock { () -> Void in
            new_exporter.exportAsynchronouslyWithCompletionHandler({
                dispatch_async(dispatch_get_main_queue(),{
                    print("Exporting... \(loopNum)")
                    self.exportDidFinish(new_exporter, loopNum: loopNum)
                })
            }) // end completion handler
        } // end block



        //prepare for next loop
        loopNum = loopNum+1
        currentDuration = currentDuration+10

    if(loopNum>=numberOfSplits){
        self.allExportsDone(Int(numberOfSplits))
        }
} // end while


}

Here's the exportDidFinish method:

func exportDidFinish(session: AVAssetExportSession, loopNum: Int) {
let outputURL: NSURL = session.outputURL!
let library: ALAssetsLibrary = ALAssetsLibrary()
if(library.videoAtPathIsCompatibleWithSavedPhotosAlbum(outputURL)) {
    library.writeVideoAtPathToSavedPhotosAlbum(outputURL, completionBlock: {(url, error) in
//done
print("Success on \(Int(loopNum))")
})
}
}

Here's the console output:

Exporting... 9
2016-08-20 13:39:27.980 TrimVideo[4776:1576022] Video /var/mobile/Containers/Data/Application/BE125EB7-AFC4-48B5-95C3-941B420BB71F/Documents/splitVideo-972-6.mp4 cannot be saved to the saved photos album: Error Domain=NSURLErrorDomain Code=-1100 "The requested URL was not found on this server." UserInfo={NSUnderlyingError=0x1457f43f0 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}, NSErrorFailingURLStringKey=file:///var/mobile/Containers/Data/Application/BE125EB7-AFC4-48B5-95C3-941B420BB71F/Documents/splitVideo-972-6.mp4, NSErrorFailingURLKey=file:///var/mobile/Containers/Data/Application/BE125EB7-AFC4-48B5-95C3-941B420BB71F/Documents/splitVideo-972-6.mp4, NSURL=file:///var/mobile/Containers/Data/Application/BE125EB7-AFC4-48B5-95C3-941B420BB71F/Documents/splitVideo-972-6.mp4, NSLocalizedDescription=The requested URL was not found on this server.}
Exporting... 9
2016-08-20 13:39:27.984 TrimVideo[4776:1576022] Video /var/mobile/Containers/Data/Application/BE125EB7-AFC4-48B5-95C3-941B420BB71F/Documents/splitVideo-972-7.mp4 cannot be saved to the saved photos album: Error Domain=NSURLErrorDomain Code=-1100 "The requested URL was not found on this server." UserInfo={NSUnderlyingError=0x1457f88c0 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}, NSErrorFailingURLStringKey=file:///var/mobile/Containers/Data/Application/BE125EB7-AFC4-48B5-95C3-941B420BB71F/Documents/splitVideo-972-7.mp4, NSErrorFailingURLKey=file:///var/mobile/Containers/Data/Application/BE125EB7-AFC4-48B5-95C3-941B420BB71F/Documents/splitVideo-972-7.mp4, NSURL=file:///var/mobile/Containers/Data/Application/BE125EB7-AFC4-48B5-95C3-941B420BB71F/Documents/splitVideo-972-7.mp4, NSLocalizedDescription=The requested URL was not found on this server.}
Exporting... 9
2016-08-20 13:39:27.988 TrimVideo[4776:1576022] Video /var/mobile/Containers/Data/Application/BE125EB7-AFC4-48B5-95C3-941B420BB71F/Documents/splitVideo-972-8.mp4 cannot be saved to the saved photos album: Error Domain=NSURLErrorDomain Code=-1100 "The requested URL was not found on this server." UserInfo={NSUnderlyingError=0x14687cb30 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}, NSErrorFailingURLStringKey=file:///var/mobile/Containers/Data/Application/BE125EB7-AFC4-48B5-95C3-941B420BB71F/Documents/splitVideo-972-8.mp4, NSErrorFailingURLKey=file:///var/mobile/Containers/Data/Application/BE125EB7-AFC4-48B5-95C3-941B420BB71F/Documents/splitVideo-972-8.mp4, NSURL=file:///var/mobile/Containers/Data/Application/BE125EB7-AFC4-48B5-95C3-941B420BB71F/Documents/splitVideo-972-8.mp4, NSLocalizedDescription=The requested URL was not found on this server.}
Exporting... 9
Success on 9
Exporting... 9
Success on 9
Exporting... 9
Success on 9
Exporting... 9
Success on 9
Exporting... 9
socca1157
  • 365
  • 3
  • 17
  • The while loop finishes before the exports because it is launching ten *asynchronous* export operations. The thread doesn't block while they complete, hence why you get to set a completion handler. What's happening in exportDidFinish? Can we see? – ncke Aug 20 '16 at 18:12
  • @ncke Thank you, I added the exportDidFinish method. – socca1157 Aug 20 '16 at 18:19
  • Thanks, looking... – ncke Aug 20 '16 at 18:25
  • What happened to exporting... 0 etc in the console log, did they appear or straight in at number 6? – ncke Aug 20 '16 at 18:35
  • @ncke Thanks, That's the entire console output, starting at 6 instead of 0. Reading over your answer now. – socca1157 Aug 20 '16 at 19:49

1 Answers1

1

Ok, some news.

  1. ALAssetsLibrary is deprecated since iOS 8, with Apple moving everyone off to the Photos Framework to do this. Good news is that AVAssetExportSession is not deprecated. While you could carry on with the deprecated API, it might be a good idea to rewrite that exportDidFinish function to use the new API.

  2. The while loop in the splitVideo function is throwing out ten concurrent export operations. Its a bit of a guess to be honest, but I suspect that there is some resource contention kicking in once you get to clip 6.

So that needs to be redesigned to be a little more friendly. Best bet is to use an NSOperationQueue with the maxConcurrentOperationsCount set to one (i.e. a serial queue).

Something like:

let opQueue = NSOperationQueue()
opQueue.maxConcurrentOperationsCount = 1

for loopNum in 0..<numberOfSplits {

   // Set up the exporter, then:

    opQueue.addOperationWithBlock { () -> Void in
        new_exporter.exportAsynchronouslyWithCompletionHandler({
            dispatch_async(dispatch_get_main_queue(),{
                print("Exporting... \(loopNum)")
                self.exportDidFinish(new_exporter, loopNum: loopNum)
            })
        } // end completion handler
    } // end block

} // end while

The purpose of this is to ensure that the export operations get run one at a time rather than an attempt at all at once. If that succeeds, you can experiment with upping the maxConcurrentOperationsCount to get some multi-threading going on.

ncke
  • 1,084
  • 9
  • 14
  • I appreciate your help thus far. I've implemented your example and the console output shows it, but strangely it's still failing 3/9 times. Console log here for character limit: https://justpaste.it/xiu0 – socca1157 Aug 20 '16 at 20:46
  • In exportDidFinish can you change the if statement's outputURL to outputURL.path like this: if(library.videoAtPathIsCompatibleWithSavedPhotosAlbum(outputURL.path)) and also add an else clause to this if statement to log if incompatible. – ncke Aug 20 '16 at 20:58
  • I had to change it slightly for it to build, but came up with: if(library.videoAtPathIsCompatibleWithSavedPhotosAlbum(NSURL(string: outputURL.path!))) The else statement, logged the last 3 clips failing. Still giving the can not be found error message. – socca1157 Aug 20 '16 at 21:08
  • Hmm, ok. Could you drop the class into justpaste? – ncke Aug 20 '16 at 21:10
  • Thanks. I need to track down an MP4 and try this for myself. It's late here (UK), ok if I come back to this tomorrow? – ncke Aug 20 '16 at 21:38
  • Absolutely, thanks again. I'm also open to using AVFileTypeQuickTimeMovie for what it's worth. – socca1157 Aug 20 '16 at 21:48
  • I've noticed if the video is less than 60 seconds long, it has 100% success rate. Any video I choose over 60 seconds will fails on the 7th clip. – socca1157 Aug 20 '16 at 23:12
  • 1
    Went on a mission: I think this is as far as I can get. http://pastebin.com/9KYMKEVZ – ncke Aug 21 '16 at 13:36
  • Thank you for this, going to work on integrating the classes now. – socca1157 Aug 22 '16 at 01:27