Mahesh Narla
Mahesh Narla

Reputation: 352

iOS objective c How to load next coming video while playing current video without delay

In my application ai have loaded all video url's to AVPlayer and it has previous and next buttons here i have setup player

    -(void)setUpMyNewPlayer
    {
            [self addTimer];
        NSURL *url=[NSURL URLWithString:_videosArray[0]];
        _currentIndex =0;
        videoPlayer = [[AVPlayer alloc]init]; //WithPlayerItem:_avPlayerItem];
        videoPlayer.automaticallyWaitsToMinimizeStalling = NO;
        AVAsset *asset = [AVAsset assetWithURL:url];
        [asset loadValuesAsynchronouslyForKeys:@[@"playable"] completionHandler:^{

            AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:asset];
            videoPlayer = [AVPlayer playerWithPlayerItem:item];
            AVPlayerLayer  *playerLayer = [AVPlayerLayer playerLayerWithPlayer:videoPlayer];
            playerLayer.videoGravity = AVLayerVideoGravityResize;
            playerLayer.frame = CGRectMake(0, 0, self.playView.frame.size.width, self.playView.frame.size.height);
            [self.playView.layer addSublayer:playerLayer];
            [videoPlayer play];
            CMTime interval = CMTimeMakeWithSeconds(0.5, NSEC_PER_SEC);
            dispatch_queue_t mainQueue = dispatch_get_main_queue();


            __weak typeof(self) weakSelf = self;
            [videoPlayer addPeriodicTimeObserverForInterval:interval
                                                      queue:mainQueue
                                                 usingBlock:^(CMTime time) {
                                                     // Use weak reference to self
                                                     if (_currentIndex==_contentImages.count-1) {
                                                         weakSelf.nextButton.hidden=YES;
                                                         weakSelf.previousButton.hidden=NO;
                                                     }
                                                     else if (_currentIndex==0)
                                                     {
                                                         weakSelf.previousButton.hidden=YES;
                                                         if (_contentImages.count>1) {
                                                             weakSelf.nextButton.hidden=NO;
                                                         }
                                                         else
                                                         {
                                                             weakSelf.nextButton.hidden=YES;
                                                         }

                                                     }
                                                     else if (_currentIndex>0 && _currentIndex!=_contentImages.count-1)
                                                     {
                                                         // NSLog(@"Showing Both");
                                                         weakSelf.nextButton.hidden=NO;
                                                         weakSelf.previousButton.hidden=NO;
                                                     }

                                                 }];
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(itemDidFinishPlaying1:) name:AVPlayerItemDidPlayToEndTimeNotification object:item];
        }];

    }

-(void)itemDidFinishPlaying1:(NSNotification *) notification {
   //  
    // Will be called when AVPlayer finishes playing playerItem
    if (_currentIndex == _videosArray.count-1) {

    }
    else{

        _currentIndex = _currentIndex+1;
        NSURL *url=[NSURL URLWithString:_videosArray[_currentIndex]];
        AVAsset *asset = [AVAsset assetWithURL:url];
        AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:asset];
        [videoPlayer replaceCurrentItemWithPlayerItem:item];
        [self changePage:UIPageViewControllerNavigationDirectionForward];
        [self addTimer];
    }

}


-(void)addTimer
{
    myTimer = [NSTimer scheduledTimerWithTimeInterval: 0.1 target: self
                                             selector: @selector(callAfterOneSecond1:) userInfo: nil repeats: YES];
}

-(void) callAfterOneSecond1:(NSTimer*)t
{
    [[AppDelegate shared] showLoading];
    if (videoPlayer.rate !=0 && videoPlayer.error == nil && videoPlayer.status == AVPlayerStatusReadyToPlay) {
        [[AppDelegate shared]removeLoading];
        [myTimer invalidate];
        myTimer=nil;
    }

}

pragma mark- PreviousAction

- (IBAction)previousButtonAction:(id)sender {

    if (_currentIndex == 0) {

    }
    else{
        _currentIndex = _currentIndex-1;
        NSURL *url=[NSURL URLWithString:_videosArray[_currentIndex]];
        AVAsset *asset = [AVAsset assetWithURL:url];
        AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:asset];
        [videoPlayer replaceCurrentItemWithPlayerItem:item];
        [videoPlayer play];
        [self changePage:UIPageViewControllerNavigationDirectionReverse];
        [self addTimer];
    }
   }

pragma mark- NextAction

- (IBAction)nextButtonAction:(id)sender {  

    if (_currentIndex == _videosArray.count-1) {

    }
    else{
        _currentIndex = _currentIndex+1;
        NSURL *url=[NSURL URLWithString:_videosArray[_currentIndex]];
        AVAsset *asset = [AVAsset assetWithURL:url];
        AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:asset];
        [videoPlayer replaceCurrentItemWithPlayerItem:item];
        [videoPlayer play];
        [self changePage:UIPageViewControllerNavigationDirectionForward];
           [self addTimer];
    }
}

Here the player is playing well but with small delay. . How to load next coming video while playing current video without delay.

Upvotes: 3

Views: 727

Answers (2)

Roman Truba
Roman Truba

Reputation: 4401

Use AVQueuePlayer instead of simple AVPlayer. At the end of current video it will preload next video.

See how to use AVQueuePlayer here: https://stackoverflow.com/a/22785665/1271424

Upvotes: 0

Goz
Goz

Reputation: 62323

I'm a bit rusty and the iOS documentation on the whole of AVFoundation is woefully inadequate if you want to do anything slightly complicated. Its been a while since I did this but something like this should work (I'm assuming you have an NSArray*, pAssets containing all your videos as AVAsset*)

// Define the composition.
AVMutableComposition*       pComposition        = [AVMutableComposition composition];

// Define the tracks in the composition.
AVMutableCompositionTrack*  pCompositionVideoTrack  = [pComposition addMutableTrackWithMediaType: AVMediaTypeVideo preferredTrackID: 1];
AVMutableCompositionTrack*  pCompositionAudioTrack  = [pComposition addMutableTrackWithMediaType: AVMediaTypeAudio preferredTrackID: 2];

CMTime time = kCMTimeZero;
for ( AVAsset* pAssetsAsset in pAssets )
{
    // Grab first video and audio tracks
    AVAssetTrack*   pAssetsAssetVideoTrack  = [pAssetsAsset tracksWithMediaType: AVMediaTypeVideo].firstObject;     
    AVAssetTrack*   pAssetsAssetAudioTrack  = [pAssetsAsset tracksWithMediaType: AVMediaTypeAudio].firstObject;

    // Get time range of entire video.
    CMTimeRange     timeRange   = CMTimeRangeMake( kCMTimeZero, timepAssetsAsset.duration );

    // Insert the entire video and audio into their respective tracks at "time".
    NSError*    pVideoError     = nil;
    NSError*    pAudioError     = nil;
    [pCompositionVideoTrack insertTimeRange: timeRange ofTrack: pAssetsAssetVideoTrack atTime: time error: &pVideoError];       
    [pCompositionAudioTrack insertTimeRange: timeRange ofTrack: pAssetsAssetAudioTrack atTime: time error: &pAudioError];

    // Move time along appropriately.
    time    = CMTimeAdd( time, pAssetsAsset.duration );
}

If you then pass the AVMutableComposition is derived from AVAsset so you can use this as normal and drop it into an AVPlayer (via an AVPlayerItem).

Now you can seek to any point in the video. Store the start/finish points of each video and you can easily seek to it.

Edit: Its ultra simple to use AVPlayer. First you need to create an AVPlayerItem .. and then you need to play it.

AVPlayerItem*   pPlayerItem = [AVPlayerItem playerItemWithAsset: pComposition];
AVPlayer*   pPlayer     = [AVPlayer playerWithPlayerItem: pPlayerItem];

Now you need to attach it to a view's layer. So from inside your ViewController do something like this:

AVPlayerLayer*  pLayer      = [AVPlayerLayer playerLayerWithPlayer: pPlayer];
[self.view.layer addSublayer: pLayer];

Upvotes: 1

Related Questions