avloopplayer/main.m

/*
 Copyright (C) 2014 Apple Inc. All Rights Reserved.
 See LICENSE.txt for this sample’s licensing information
 
 Abstract:
 
  
  Command line tool for playing audiovisual media in loop
  
  
 */
 
@import Foundation;
@import AVFoundation;
@import CoreMedia;
 
static void* const AVLoopPlayerCurrentItemObservationContext = (void*)&AVLoopPlayerCurrentItemObservationContext;
 
@interface AVLoopPlayer : NSObject
{
@private
    AVQueuePlayer *_queuePlayer;
}
 
- (void)playbackInLoopWithURL:(NSURL *)URL;
- (void)stopPlayback;
 
@end
 
@implementation AVLoopPlayer
 
- (id)init
{
    self = [super init];
    if (self)
    {
        _queuePlayer = [[AVQueuePlayer alloc] init];
    }
    
    return self;
}
 
- (void)playbackInLoopWithURL:(NSURL *)URL
{
    AVURLAsset *asset = [AVURLAsset assetWithURL:URL];
    
    [asset loadValuesAsynchronouslyForKeys:@[@"duration"] completionHandler:^{
        NSError *error;
        // Check to make sure duration is loaded before accessing its value.
        AVKeyValueStatus durationStatus = [asset statusOfValueForKey:@"duration" error:&error];
        
        switch (durationStatus)
        {
            case AVKeyValueStatusLoaded:
            {
                // The asset invokes its completion handler on an arbitrary queue when loading is complete.
                // Because we want to access our AVQueuePlayer in our ensuing set-up, we must dispatch our handler to the main queue.
                dispatch_async(dispatch_get_main_queue(), ^{
                    // Based on the duration of the asset, we decide the number of player items to add to demonstrate gapless playback of the same asset
                    NSUInteger countOfPlayerItems = (1.0 / CMTimeGetSeconds([asset duration])) + 2;
                    for (NSUInteger idx = 0; idx < countOfPlayerItems; ++idx)
                    {
                        AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset];
                        if (playerItem)
                        {
                            [_queuePlayer insertItem:playerItem afterItem:nil];
                        }
                    }
                    
                    [_queuePlayer addObserver:self forKeyPath:@"currentItem" options:NSKeyValueObservingOptionOld context:AVLoopPlayerCurrentItemObservationContext];
                    [_queuePlayer play];
                });
                
                break;
            }
            case AVKeyValueStatusFailed:
            {
                NSLog(@"Failed to load duration property for asset: %@ with error: %@", asset, error);
                break;
            }
            default:
                break;
        }
    }];
}
 
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)changeDictionary context:(void *)context
{
    if (context == AVLoopPlayerCurrentItemObservationContext)
    {
        AVQueuePlayer *player = (AVQueuePlayer *)object;
        
        // Append the previous current item to the player's queue
        AVPlayerItem *itemRemoved = changeDictionary[NSKeyValueChangeOldKey];
        
        // An initial change from a nil currentItem yields NSNull here.
        // Check to make sure the class is AVPlayerItem before appending it to the end of the queue
        if ([itemRemoved isKindOfClass:[AVPlayerItem class]])
        {
            [itemRemoved seekToTime:kCMTimeZero];
            [player insertItem:itemRemoved afterItem:nil];
        }
    }
}
 
- (void)stopPlayback
{
    [_queuePlayer pause];
    [_queuePlayer removeObserver:self forKeyPath:@"currentItem" context:AVLoopPlayerCurrentItemObservationContext];
    [_queuePlayer removeAllItems];
}
 
@end
 
int main(int argc, const char * argv[])
{
    @autoreleasepool
    {
        if (argc != 2)
        {
            NSLog(@"Usage: %s <path-to-movie>",argv[0]);
            return 1;
        }
        
        NSString *filePath = [[NSString alloc] initWithUTF8String:argv[1]];
        NSURL *fileURL = [NSURL fileURLWithPath:filePath];
        
        AVLoopPlayer *player = [[AVLoopPlayer alloc] init];
        [player playbackInLoopWithURL:fileURL];
        
        // Play for atleast 3 seconds
        NSDate *timeOut = [NSDate dateWithTimeIntervalSinceNow:3.0];
        [[NSRunLoop mainRunLoop] runUntilDate:timeOut];
        
        [player stopPlayback];
        
        return 0;
    }
    return 0;
}