HysteriaPlayer provides useful basic player functionalities.
It provides:
- PlayerItem cache management.
- Pre-buffer next PlayerItem.
Features:
- Supporting both local and remote media.
- You don't need to write KVO again, setting up few blocks then you can handle player status.
- Ability to play previous PlayerItem.
- If player suspended bacause of high network latency in bad network, auto-resume the playback of your PlayerItem when buffered ready.
- Background playable enabled. (need to register your App supports background modes as "App plays audio")
- Using getHysteriaOrder: to get the index of your PlayerItems.
- Extends long time buffering in background.
- Returns playing item's current and duration timescale.
- PlayModes: Repeat, RepeatOne, Shuffle.
In part 0, what we want? why HysteriaPlayer?
In part 1, demonstrating how to play remote audios with HysteriaPlayer.
In part 2, making a simple player user interface.
In part 3, registering lock screen details and remote controls.
You can download tutorial source code here
If you using CocoaPods, it's easy to install HysteriaPlayer.
Podfile:
platform :ios, 'x.0'
pod 'HysteriaPlayer', '~> x.x.x'
end
Drag HysteriaPlayer.m
, HysteriaPlayer.h
to your project.
Add CoreMedia.framework, AudioToolbox.framework and AVFoundation.framework to your Link Binary With Libraries.
Ability to play the first PlayerItem when your application is resigned active but first PlayerItem is still buffering.
Click your project and select your target app, going to the info tab find Required background modes , if not exist create new one. In Required background modes's item 0 copy this string App plays audio
into it.
Register Handlers of HysteriaPlayer, all Handlers are optional.
#import "HysteriaPlayer.h"
...
- (void)setupHyseteriaPlayer
{
HysteriaPlayer *hysteriaPlayer = [HysteriaPlayer sharedInstance];
[hysteriaPlayer registerHandlerPlayerRateChanged:^{
} CurrentItemChanged:^(AVPlayerItem *item) {
} PlayerDidReachEnd:^{
}];
[hysteriaPlayer registerHandlerCurrentItemPreLoaded:^(CMTime time) {
NSLog(@"%f", CMTimeGetSeconds(time));
}];
[hysteriaPlayer registerHandlerReadyToPlay:^(HysteriaPlayerReadyToPlay identifier) {
switch (identifier) {
case HysteriaPlayerReadyToPlayCurrentItem:
if ([hysteriaPlayer getHysteriaPlayerStatus] != HysteriaPlayerStatusForcePause) {
[hysteriaPlayer play];
}
break;
case HysteriaPlayerReadyToPlayPlayer:
[hysteriaPlayer play];
break;
default:
break;
}
}];
[hysteriaPlayer registerHandlerFailed:^(HysteriaPlayerFailed identifier, NSError *error) {
switch (identifier) {
case HysteriaPlayerFailedCurrentItem:
break;
default:
break;
}
}];
[hysteriaPlayer setPlayerRepeatMode:RepeatMode_off];
[hysteriaPlayer enableMemoryCached:NO];
}
Specified event would trigger related callback blocks.
PlayerRateChanged's callback block will be called when player's rate changed, probely 1.0 to 0.0 or 0.0 to 1.0. You should update your UI to notice the user what's happening. HysteriaPlayer have getHysteriaPlayerStatus: method helping you find out the informations.
- HysteriaPlayerStatusPlaying : Player is playing
- HysteriaPlayerStatusForcePause : Player paused when Player's property
PAUSE_REASON_ForcePause = YES
. - HysteriaPlayerStatusBuffering : Player suspended because of no buffered.
- HysteriaPlayerStatusUnknown : Player status unknown.
CurrentItemChanged's callback block will be called when player's currentItem changed. If you have UI elements related to Playing item, should update them.(i.e. title, artist, artwork ..)
PlayerDidReachEnd's callback block will be called when player stops, reaching the end of playing queue and repeat is disabled.
It will be called when Player or PlayerItem ready to play.
It will be called when receive new buffer data.
It will be called when Player or PlayerItem failed.
Before you starting play anything, you have to set up your data source for HysteriaPlayer. When Player gonna use (instantly play or pre-buffer) items, the source getter block will telling which index of your playing list is needed.
There are two methods to set up Source Getter.
- setupSourceGetter:ItemsCount:
- asyncSetupSourceGetter:ItemsCount:
ItemsCount tells HysteriaPlayer the counts of your data source, you have to update it using setItemsCount:(NSUInteger)count
if your datasource's count is changed.
The simplest way.
When player ask for an index that it would liked to use, return your source link as NSURL type inside the index given block.
example:
[hysteriaPlayer setupSourceGetter:^NSURL *(NSUInteger index) {
return [urlArray objectAtIndex:index];
} ItemsCount:[mp3Array count]];
For advanced usage, if you could use setupSourceGetter:ItemsCount:
as well then no needs to use this method to setting up.
If you have to access your media link when player actually gonna play that item. You probability take that media link by an asynchronous connection and HysteriaPlayer also needs an asynchronous block to transform the media link your provided to AVPlayerItem. There are no ways you can return values from an async block to another.
So, you have to call setupPlayerItem:Order:
by yourself when your async connection that getting media link is completion. And the Order parameter is what player asked for.
example:
NSUInteger count = [listItems count];
[hysteriaPlayer asyncSetupSourceGetter:^(NSUInteger index) {
asyncOperation^{
..
operation
..
NSString *mediaLink = source;
NSURL *url = [NSURL URLWithString:mediaLink];
[hysteriaPlayer setupPlayerItem:url Order:index];
}
} ItemsCount:count];
HysteriaPlayer *hysteriaPlayer = [HysteriaPlayer sharedInstance];
NSNumber *order = [hysteriaPlayer getHysteriaOrder:[hysteriaPlayer getCurrentItem]];
HysteriaPlayer *hysteriaPlayer = [HysteriaPlayer sharedInstance];
NSDictionary *dict = [hysteriaPlayer getPlayerTime];
double durationTime = [[dict objectForKey:@"DurationTime"] doubleValue];
double currentTime = [[dict objectForKey:@"CurrentTime"] doubleValue];
pausePlayerForcibly:(BOOL)
method telling HysteriaPlayer to/not to force pause the playback(mostly when user tapped play/pause button)
- (IBAction)play_pauseButton:(id)sender
{
HysteriaPlayer *hysteriaPlayer = [HysteriaPlayer sharedInstance];
if ([hysteriaPlayer isPlaying])
{
[hysteriaPlayer pausePlayerForcibly:YES];
[hysteriaPlayer pause];
}else{
[hysteriaPlayer pausePlayerForcibly:NO];
[hysteriaPlayer play];
}
}
switch ([hysteriaPlayer getHysteriaPlayerStatus]) {
case HysteriaPlayerStatusUnknown:
break;
case HysteriaPlayerStatusForcePause:
break;
case HysteriaPlayerStatusBuffering:
break;
case HysteriaPlayerStatusPlaying:
default:
break;
}
Default is cache enabled
HysteriaPlayer *hysteriaPlayer = [HysteriaPlayer sharedInstance];
[hysteriaPlayer enableMemoryCached:NO];
HysteriaPlayer *hysteriaPlayer = [HysteriaPlayer sharedInstance];
[hysteriaPlayer deprecatePlayer];
hysteriaPlayer = nil;
All source code is licensed under the MIT License.
Created by Saiday