0

My brain feels like scrambled eggs... I'm trying to merge video clips together. I have each clip URL stored in an NSMutableArray (arrayClipURL) & that's all good. When I print timeRanges & tracks (both NSMutableArrays) in debug console, everything checks out, which means for loop is doing its job. I keep getting an exception error: '* -[__NSArrayM insertObject:atIndex:]: object cannot be nil'.

I threw in an exception breakpoint and its breaking at the last line below. I cant figure it out because both timeRanges and tracks are NOT nil... I can print them in the debug console and see them just fine directly before the line that breaks.

composition = [[AVMutableComposition composition] init];

composedTrack = [[AVMutableCompositionTrack alloc] init];
composedTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo
                                         preferredTrackID:kCMPersistentTrackID_Invalid];

NSMutableArray * timeRanges = [[NSMutableArray alloc] initWithCapacity:arrayClipURL.count];
NSMutableArray * tracks = [[NSMutableArray alloc] initWithCapacity:arrayClipURL.count];

for (int i=0; i<[arrayClipURL count]; i++){
    AVURLAsset *assetClip = [[AVURLAsset alloc] initWithURL:[arrayClipURL objectAtIndex:i] options:nil];
    AVAssetTrack *clipTrack = [[AVAssetTrack alloc] init];
    clipTrack = [[assetClip tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
    [timeRanges addObject:[NSValue valueWithCMTimeRange:CMTimeRangeMake(kCMTimeZero, assetClip.duration)]];
    [tracks addObject:clipTrack];
}
debug(@"timeranges:  %@",timeRanges);
debug(@"tracks:  %@",tracks);

[composedTrack insertTimeRanges:timeRanges ofTracks:tracks atTime:kCMTimeZero error:nil]; 

I am in need of some veteran assistance :( Any idea what could be causing the problem?

Edit: Here are how the 2 arrays look printed in the console. The only thing I can think of is maybe the CMTimeRange or AVAssetTrack aren;t formatted properly in the arrays??? i have no idea. Just thought it might help to see what it's actually trying to insert when the exception is thrown.

    2013-02-18 13:18:20.811 app[5242:907] [AVCaptureManager.m:401] timeranges array:  (
    "CMTimeRange: {{0/1 = 0.000}, {498/600 = 0.830}}",
    "CMTimeRange: {{0/1 = 0.000}, {556/600 = 0.927}}"
)
2013-02-18 13:18:20.812 app[5242:907] [AVCaptureManager.m:402] tracks array:  (
    "<AVAssetTrack: 0x220c21a0, trackID = 1, mediaType = vide>",
    "<AVAssetTrack: 0x1cdec820, trackID = 1, mediaType = vide>"
)
Daniel McCarthy
  • 1,406
  • 2
  • 13
  • 19
  • In your `for`-loop, the condition contains `[arrayMovieUrl count]` - is this just a typo and should it say `[arrayClipURL count]`? – Michael Rose Feb 18 '13 at 19:17
  • Just edited the question and added some info on the 2 arrays that are causing the problem. Not sure why, but my gut tells me it might be an issue with the format of the timeRanges array. – Daniel McCarthy Feb 18 '13 at 20:08

1 Answers1

1

Your problem is caused by the scope of your AVURLAsset instances

Since you AVURLAsset *assetClip in inside the for loop, it is not valid outside of it and neither is the tracks you extracted.

If you keep your assetClip in an array that survives the scope of your for loop, it should fix your problem

MLefrancois
  • 768
  • 5
  • 12
  • I thought thats what I was doing by creating & init the assetClip & tracks arrays before (outside) the for loop, then adding the AVURLAssets & CMTimeRanges inside the loop. I thought this was working, since when i console/debug print both of those arrays outside the for loop, it actually shows the correct data. How should I do it differently so the arrays properly survive the scope of the loop? – Daniel McCarthy Feb 18 '13 at 20:23
  • The problem is that AVURLAsset *assetClip (the actual AVURLAsset) is not surviving the scope of the for loop. I don't remember where i read it, but the AVAssetTrack instances stay valid only if their "parent" AVAsset are still alive. Try adding the actual AVURLAsset to an array to they too survive the for loop scope – MLefrancois Feb 18 '13 at 23:57
  • This was 1/2 the problem, but I'll accept the answer. The real issue was that assetClip kept getting overwritten with each iteration of the loop. So regardless of how many times the loop ran, at the end, it was always trying to compose multiple tracks with a single assetClip. Im not even going to post my work around, cuz its quite dirty & probably not the best way to fix... but instead of a loop, I just put 20 if statements inside one another, checking the track count and adding new assetClip1, assetClip2...3..4 etc when needed. & capping the number of segments @20, messy but works for now – Daniel McCarthy Feb 25 '13 at 17:28
  • if you know how I can fix this more eloquently, I'd love some advice. I can create a new question if you want to answer it. (The only thing i can think of is somewhow creating new/unique AVURLAssets at runtime, correlating to the number of tracks being added to the composition.. but im not sure thats possible) – Daniel McCarthy Feb 25 '13 at 17:30
  • Have you tried creating the AVURLAsset *assetClip in another loop and keeping them in an NSArray then using them in a second loop to create your AVAssetTracks ? – MLefrancois Feb 25 '13 at 18:44