AVQueuePlayerDemo
demo for customize the ios player
Install / Use
/learn @finalevil/AVQueuePlayerDemoREADME
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) {
}
Related Skills
node-connect
345.9kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
106.4kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
345.9kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
345.9kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
