Search code examples
iphoneobjective-cloopsavplayeravmutablecomposition

Can't get AVMutableComposition in loop to play out of loop


Xcode for iOS

The issue that I'm having is in the playback of an AVMutableComposition. I'm creating the composition then adding AVURLAssets to it inside a for loop. I'm adding everything as I would similarly add an object to a mutable array and then attempting to play it back afterwards, but playback isn't occuring. Here is my code:

AVMutableComposition *composition = [AVMutableComposition composition];

int count;

count = array.count;

for (int y=0;y<count;y++){    

        NSURL *url;

            url = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/%@.mp3", [[NSBundle mainBundle] resourcePath],[array objectAtIndex:y]]];





        NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:AVURLAssetPreferPreciseDurationAndTimingKey];

        AVURLAsset *sourceAsset = [[AVURLAsset alloc] initWithURL:url options:options];

        //calculate times
        NSNumber *time = [soundArray1 objectAtIndex:1];

        double timenow = [time doubleValue];
        double insertTime = (240*y);

        CMTime douTime = CMTimeMake(240, timenow);
        CMTime staTime = CMTimeMake(0, 1);
        CMTimeRange editRange = CMTimeRangeMake(staTime,douTime);

        CMTime insTime = CMTimeMake(insertTime, timenow);


        NSError *editError;


        [composition insertTimeRange:editRange ofAsset:sourceAsset atTime:insTime error:&editError];

    }


AVPlayer* mp = [AVPlayer playerWithPlayerItem:[AVPlayerItem playerItemWithAsset:composition]];


[mp play];

I'd assume that I am getting it completely wrong, but I then added this inside the loop instead of after, inside an if and opening a NSRunLoop to check (a little bad, but it was just to test!):

    AVMutableComposition *composition = [AVMutableComposition composition];

int count;

count = array.count;

for (int y=0;y<count;y++){    

        NSURL *url;

            url = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/%@.mp3", [[NSBundle mainBundle] resourcePath],[array objectAtIndex:y]]];





        NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:AVURLAssetPreferPreciseDurationAndTimingKey];

        AVURLAsset *sourceAsset = [[AVURLAsset alloc] initWithURL:url options:options];

        //calculate times
        NSNumber *time = [soundArray1 objectAtIndex:1];

        double timenow = [time doubleValue];
        double insertTime = (240*y);

        CMTime douTime = CMTimeMake(240, timenow);
        CMTime staTime = CMTimeMake(0, 1);
        CMTimeRange editRange = CMTimeRangeMake(staTime,douTime);

        CMTime insTime = CMTimeMake(insertTime, timenow);


        NSError *editError;


        [composition insertTimeRange:editRange ofAsset:sourceAsset atTime:insTime error:&editError];



    if(y==3){

    AVPlayer* mp = [AVPlayer playerWithPlayerItem:[AVPlayerItem playerItemWithAsset:composition]];


    [mp play];


    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:20]];

        }
    }

Here, I just gave my array 4 elements and made it play the composition on the 4th run-through. It worked and played my 4 audio files in perfect order.

What do I need to do to make my composition play after this loop rather than in it? Am I mis-using the system?

EDIT: by sticking my open RunLoop code on the end, it plays. Should I be doing it this way, or is there a better way to do this?


Solution

  • So I was going about this the wrong way. I needed to create an AVMutableCompositionTrack for each element and add that to my composition

           NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES]
                                                                forKey:AVURLAssetPreferPreciseDurationAndTimingKey];
    
            AVURLAsset *sourceAsset = [[AVURLAsset alloc] initWithURL:url options:options];
    
            //calculate times
            NSNumber *time = [soundArray1 objectAtIndex:1];
    
            double timenow = [time doubleValue];
    
            double insertTime = (240*y);
    
                        AVMutableCompositionTrack *track =
            [composition addMutableTrackWithMediaType:AVMediaTypeAudio
                                     preferredTrackID:kCMPersistentTrackID_Invalid];
    
            //insert the audio track from the asset into the track added to the mutable composition
            AVAssetTrack *myTrack = [[sourceAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
            CMTimeRange myTrackRange = myTrack.timeRange;
            NSError *error = nil;
            [track insertTimeRange:myTrackRange
                                          ofTrack:myTrack
                                           atTime:CMTimeMake(insertTime, timenow) 
                                            error:&error];
    
            [sourceAsset release];
    

    followed by

    AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:composition];
    self.player = [[AVPlayer alloc] initWithPlayerItem:playerItem];
    
    
            [self.player play];
    

    with player declared as a property in the interface section

    @interface SecondViewController ()
    
    
    
    @property (nonatomic, assign) AVPlayer *player;
    
    
    
    @end