Skip to content

finalevil/AVQueuePlayerDemo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 

Repository files navigation

AVQueuePlayerDemo

(welcome to visit my blog: http://blog.finalevil.com)

在 AppDelegate 宣告 AVQueuePlayer

###1. include 3 framework

CoreMedia.framework for CMTime type
AudioToolbox.framework for AVAudioSession
AVFoundation.framework for AVQueuePlayer

###2. for background task (1)在app_name.plist中加入一個key值

     <key>UIBackgroundModes</key>
     <array>
          <string>audio</string>
     </array>

(2)add the code in didFinishLaunchingWithOptions

[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];

(3)當程式進入背景,觸發timer

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(resumePlayback) userInfo:nil repeats:NO];
}

(4)當播放器進入背景模式,一定會自動停止播放,所以我們必須透過一個timer不斷檢查播放器是否應該要播放,如果應該播放,就執行play的動作。

-(void)resumePlayback {
    [queuePlayer play];
}

當播放器開始進入背景播放的狀態下,就不會停止,會一直處於此一模式之下。

###3. for play video (1)add AVPlayerLayer to show video

    AVPlayerLayer *playerLayer = [[AVPlayerLayer alloc] init];
    [playerLayer setFrame:self.playerView.frame];
    [playerView.layer addSublayer:playerLayer];
    [playerLayer setPlayer:appDelegate.queuePlayer];

(2)add video item to queue

    playlistData = [[NSMutableArray alloc] init];   
    for (int i=1; i<=11; i++) {
        NSString *videoPath1 = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"%d", i] ofType:@"mp4"];
        AVPlayerItem *videoItem1 = [AVPlayerItem playerItemWithURL:[NSURL fileURLWithPath:videoPath1]];
        [playlistData addObject:videoItem1];
    }
    appDelegate.queuePlayer = [AVQueuePlayer queuePlayerWithItems:[NSArray arrayWithArray:playlistData]];   

(3)call

[player play];

###4. sync UI (ex: slider, lablel for current time, label for duration) 兩種不同狀況,第一種事針對影片與影片間切換後的sync,第二種是針對影片播放過程中的sync (1)對每一個avplayeritem設定notification

    for (int i=0; i<[playlistData count]; i++) {
       
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(itemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:[playlistData objectAtIndex:i]];
       
    }

因此每當一個影片播放結束就會觸發一次此一事件。 在事件處理中,寫下對應的處理內容。原則上就是取得下一個item(影片)的時間長度,重設slider的max value並將slider歸零。

-(void)itemDidFinishPlaying :(NSNotification *)notification {
   
   
    AVPlayerItem *p = [notification object];

    AVPlayerItem *nextItem;
    float timeDuration, timeCurrent;
    for (int i=0; i<[playlistData count]; i++) {
        if ([p isEqual:[playlistData objectAtIndex:i]]) {
           
            if ((i+1) < [playlistData count]) {
                nextItem = [playlistData objectAtIndex:i+1];
                timeDuration = CMTimeGetSeconds(nextItem.duration);
                timeCurrent = CMTimeGetSeconds(nextItem.currentTime);
                break;
               
            }
           
        }
    }
   
    // Will be called when AVPlayer finishes playing playerItem
   

    NSLog(@"%f", timeDuration);
    [sliderTimeSeeker setMaximumValue:timeDuration];
    [sliderTimeSeeker setValue:timeCurrent];

    lblCurrentTime.text = @"0";
    lblDuration.text = [NSString stringWithFormat:@"%d", (int)timeDuration];

    [sliderTimeSeeker setValue:0];

}

(2)每隔一段時間檢查一次現在播放的currentTime,並顯示到slider和label上

    CMTime interval = CMTimeMake(3000, 1000);
    [appDelegate.queuePlayer addPeriodicTimeObserverForInterval:interval queue:dispatch_get_current_queue() usingBlock:^(CMTime time) {
       
        float seconds = CMTimeGetSeconds(appDelegate.queuePlayer.currentItem.currentTime);
        lblCurrentTime.text = [NSString stringWithFormat:@"%d", (int)seconds];
       
        CMTime endTime = CMTimeConvertScale (appDelegate.queuePlayer.currentItem.asset.duration, appDelegate.queuePlayer.currentTime.timescale, kCMTimeRoundingMethod_RoundHalfAwayFromZero);
        if (CMTimeCompare(endTime, kCMTimeZero) != 0) {
            NSLog(@"%f", CMTimeGetSeconds(appDelegate.queuePlayer.currentItem.currentTime));
            sliderTimeSeeker.value = CMTimeGetSeconds(appDelegate.queuePlayer.currentItem.currentTime);
           
        };
       
    }];

###5. 當使用者拖拉slider,即會播放對應的影片片段 寫在slider的value change 事件中

-(IBAction)handleSliderSeek:(id)sender{

    UISlider *s = (UISlider*)sender;
    [appDelegate.queuePlayer seekToTime:CMTimeMakeWithSeconds(s.value, appDelegate.queuePlayer.currentTime.timescale)];
}

###6. loop the playlist 沒有比較好的方式可以做這件事情,所以只好通過一個timer不停地去檢查看看queue中的影片是否都已經播放完畢。 queue count = 0表示播放完畢。我們可以再次將影片列表加入queue中,並且再次呼叫 [player play];

(1)在viewDidLoad中建立一個timer

  [NSTimer scheduledTimerWithTimeInterval:1.0
                target:self
              selector:@selector(checkPlaybackTime:)
              userInfo:nil
               repeats:YES];

(2)在處理timer事件的callback function中,檢查影片是否都已經播放完畢。

- (void) checkPlaybackTime:(NSTimer *)theTimer{

    if([appDelegate.queuePlayer.items count] == 0){
        [self initPlayer];
        [appDelegate.queuePlayer play];
    }
   
}

###7. 當播放器處於background,螢幕上鎖後,雙擊home鍵,可以看到一個簡易的播放控制器,想要透過這個控制器遙控你的播放器的話,必須加上兩個東西。 (1)in AppDelegate 的 didFinishLaunchingWithOptions加入此行,表示開始此app開始接收remote control的event。

[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];

(2)事件觸發後的處理函式。

- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
   
    NSLog(@"%@", event);
   
    if (event.type == UIEventTypeRemoteControl) {

        switch (event.subtype) {
            case UIEventSubtypeRemoteControlTogglePlayPause:
                break;
            default:
                break;
        }

    }
}

###8. handle the interrupt,when phone call

(1)set the delegate

(2)當結束interrupt的狀況後,會fire endInterruptionWithFlags 函式。

- (void)endInterruptionWithFlags:(NSUInteger)flags {

    //check the status, then play
    [queuePlayer play];
}

9.enable AirPlay support

[appDelegate.queuePlayer setAllowsAirPlayVideo:YES];

10. in lock screen mode, to display "now playing" information

(1)available iOS 5.0 later (2)add MediaPlayer.framework (3)include MediaPlayer.h

#import <MediaPlayer/MediaPlayer.h>

(4)call the "MPNowPlayingInfoCenter", when the video/song change

-(void)setNowPlayingInfo:(int)currentInx{

    Class playingInfoCenter = NSClassFromString(@"MPNowPlayingInfoCenter");
    
    if (playingInfoCenter) {
        
        NSMutableDictionary *songInfo = [[NSMutableDictionary alloc] init];
        
        MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:@"cover1"]];
        
        [songInfo setObject:@"music title" forKey:MPMediaItemPropertyTitle];
        [songInfo setObject:@"author" forKey:MPMediaItemPropertyArtist];
        [songInfo setObject:@"album title" forKey:MPMediaItemPropertyAlbumTitle];
        [songInfo setObject:albumArt forKey:MPMediaItemPropertyArtwork];
        
        [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songInfo];
        
    }

}

需要特別注意的是,以下這個部分是用來檢查 "MPNowPlayingInfoCenter" 這個class是否存在,因為在iOS 5.0之前的版本是不支援這個功能的(也就是這個class不存在)

Class playingInfoCenter = NSClassFromString(@"MPNowPlayingInfoCenter");
    
if (playingInfoCenter) {
}

About

demo for customize the ios player

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published