Offline playback
The Feed Media SDK now supports offline music playback. This is done by exposing to SDK users a list of stations available for offline playback and a new method to download the contents of those stations. Offline stations, once downloaded, can be played using the exact same controls as streaming music stations. Reporting of offline music playback is handled invisibly by the SDK when Internet connectivity is available. The examples below shows iOS examples but same principles apply to Android as well.
Overview
The stationList property of the FMAudioPlayer holds a list of all the stations that are available for playback, and that list is populated only after the SDK was able to contact the Feed.fm servers. The new version of the SDK exposes two additional lists of stations to support offline music playback: localOfflineStationList and remoteOfflineStationList.
remoteOfflineStationList
holds a list of stations that can be
downloaded for offline playback. This list is only available when
the SDK is able to contact the Feed.fm servers, so it is populated
at the same time that stationList
is populated, and is otherwise
an empty list. The stations in this list cannot be assigned to
activeStation
for playback - instead they must be
passed to downloadAndSyncStation:withDelegate:, which will download
the contents of the station and then add an entry to
localOfflineStationList
. The remoteOfflineStationList
stations
may be presented to users to select music they want to download, or
apps may choose to download the contents of all of these stations.
localOfflineStationList
holds a list of stations that have music
stored locally that can be assigned to activeStation
for immediate
playback. This list differs from stationList
and remoteOfflineStationList
in that the array is populated as soon as the FMAudioPlayer
is
constructed, and before any attempt is made to contact Feed.fm, so
this propery can be used even when there is no Internet connection.
Playback of offline music works exactly like streaming music.
The play
, pause
, skip
, e.t.c. methods function exactly the
same as with streaming music, so any music playback interface will
work just a well online as offline.
The FMAudioPlayer has an additional state:
FMAudioPlayerPlaybackStateOfflineOnly
. This state indicates that
the SDK is not able to contact the Feed.fm servers, but music had
been previously stored on the device and is immediately available
for playback.
Sample
This demo app has enough code to download an offline station and begin playback. If the app is run while offline, it will play any music it previously downloaded. If the app is run while online, it will contact Feed.fm to make sure it has a local copy of the first offline station available to it.
First, we initialize the library by passing our credentials to the SDK. We'll use some demo credentials that point to a demo app:
[FMAudioPlayer setClientToken:@"offline" secret:@"offline"];
That causes the SDK to contact Feed.fm and retrieve the list of available stations and offline stations.
We'll know when the app has reached Feed.fm via the whenAvailable:notAvailable
callback:
[[FMAudioPlayer sharedPlayer] whenAvailable:^{
// streaming stations are available here, as is the list of downloadable stations
FMAudioPlayer *player = [FMAudioPlayer sharedPlayer];
// list out stations available for download
for (FMStation *station in player.remoteOfflineStationList) {
NSLog(@"offline station: %@", station.name);
}
// download/update the first available offline station
FMStation *station = player.remoteOfflineStationList[0];
[player downloadAndSyncStation:station withDelegate:self];
} notAvailable:^{
// ...
}];
When the whenAvailable:
function is called, the remoteOfflineStationList
has been populated with a list of stations. We take the first station and
ask the player to download its contents. This AppDelegate class implements
FMStationDownloadDelegate and is passed as the delegate to
downloadAndSyncStation:withDelegate
to receive notice while files are downloaded.
Downloading events are sent to the `stationDownloadProgress:pendingCount:failedCount:totalCount method:
- (void)stationDownloadProgress:(FMStation *)station pendingCount:(int)pendingCount failedCount:(int)failedCount totalCount:(int)totalCount {
NSLog(@"Station download in progress.. %d of %d files remaining to download", pendingCount, totalCount);
}
When all the files in the station have been downloaded, a final call is made to
stationDownloadComplete:
, where we tune the player to the offline station and
begin music playback:
- (void)stationDownloadComplete:(FMStation *)station {
FMAudioPlayer *player = [FMAudioPlayer sharedPlayer];
player.activeStation = station;
[player play];
}
When you first run the app, it will spend some time pulling down the songs in that first station and then begin playback. Future runs will happen more quickly, as the client will only download updates to the station it has downloaded.
If the app starts up and is unable to contact feed.fm, it calls the notAvailable:
callback:
[[FMAudioPlayer sharedPlayer] whenAvailable:^{
...
} notAvailable:^{
// couldn't contact feed.fm - we must be offline!
FMAudioPlayer *player = [FMAudioPlayer sharedPlayer];
// play the first station we've downloaded
if (player.localOfflineStationList.count > 0) {
player.activeStation = player.localOfflineStationList[0];
[player play];
}
}];
In this case, we serach for the station we previously downloaded and immediately begin playback in that station.
Reporting
Feed.fm records play counts for licensing purposes. This information is recorded on the device while offline. When Internet connectivity becomes available, the SDK will automatically send the reporting events to feed.fm and remove them from the local device.
Expiration
Offline content has varying expiration dates. Every time the SDK is initialized, it asks the server to extend the expiration date on downloaded content. If the server cannot be reached, and the expiration date is hit, then the files will automatically be deleted off the device and the offline station will no longer be available.
Target Minutes
There is a variant of the downloading function called
downloadAndSyncStation:forTargetMinutes:withDelegate:
. The targetMinutes
argument is used to ask the server to limit the amount of music transferred
during this request. If the app only plans to play up to 10 minutes of
music while offline, this number can be passed to the server to ensure the
app doesn't download the full contents of a potentially large offline station
all at once. The SDK might not download any music if it already has enough.
The SDK might download 10 minutes of music, even if it already has music,
to swap out old songs the user has heard with newer songs.