Amazon Music Device API
Playback
Basics
Tracks and Track Containers A track is the basic unit of music managed by the API. Every track belongs to a track container, an abstraction of a music collection such as a playlist, a station, or an album.
Track Container Chunks A track container specifies which tracks to play and in what order. The ordering of tracks in a track container is modelled as a chain of track container chunks, each specified by a TrackContainerChunkDescription. A track container chunk contains a list of track instances to be played in order, and contains TrackPointer objects that point to the previous and next chunk in the chain.
Track container chunks within a chain may overlap. For that reason, the track pointers that link track container chunks together in the chain also indicate a particular track within a chunk’s track list:
Below is a sample JSON response for a track container chunk:
"trackContainerChunkDescriptions": {
"chunk": {
"firstTrackPointer": {
"chunk": "#chunk",
"indexWithinChunk": 0
},
"nextTrackPointer": null,
"prevTrackPointer": null,
"lastTrackPointer": {
"chunk": "#chunk",
"indexWithinChunk": 11
},
"title": "Love Sux [Explicit]",
"trackInstances": [
"#track_inst_0",
"#track_inst_1",
"#track_inst_2",
"#track_inst_3",
"#track_inst_4",
"#track_inst_5",
"#track_inst_6",
"#track_inst_7",
"#track_inst_8",
"#track_inst_9",
"#track_inst_10",
"#track_inst_11"
],
}
},
Repeat Mode
The beginning and end of a chain of track container chunks calls for special behavior that depends on whether repeat mode is active. When repeat mode is active, the chain should be treated as circular, advancing playback from the end of the chain to the beginning, and allowing the user to skip backwards from the beginning to the end. To support this feature, track container chunks contain links to the first and last track instance in the ordering.
Some containers are potentially infinite and thus have no end (e.g. stations); some are generated dynamically and do not retain enough history to always have a beginning (also e.g. stations). Client applications can determine whether or not a track container supports repeat mode by examining the firstTrackPointer
and lastTrackPointer
members of any TrackContainerChunkDescription; repeat mode is supported if and only if both members are non-null.
Track Containers and Navigation Nodes
A navigation node is essentially a list of items, and a track container is essentially a list of tracks. It is important to acknowledge that these two concepts are not mutually exclusive. The key difference is that the items in a navigation node are presented to the user as a list of choices, whereas tracks in a track container are simply played in order. For some Navigation Nodes (such as the root node, initiating playback may not make sense, and for that reason such navigation nodes are not also track containers. To give another example, Amazon Music Stations are track containers that do not require track selection and for that reason they are not also navigation nodes.
Playback Event Reporting
Each track instance in a response will contain a track definition with a playback event collector inside. Applications must post event JSON data to the event collector for every applicable customer action linked to a playback event. Event data is posted in the form of a PlaybackEventReport. Some examples of playback events are starting playback, pausing playback, or stopping playback due to closing the application. There are two main playback event report types, Start and Stop. Each of those events supports multiple reason codes (see below sections for a complete list of reason codes).
To the extent possible, playback events should be reported from the user’s perspective. In particular, when the user chooses to play a track and has to wait a few seconds for initial buffering, the start event should not be reported until the buffering has finished, and the delay reported in playbackDelay
field of the playbackEventReport
.
Let’s look at some example JSON. Suppose the API response included the following JSON for a given track:
"track_inst_91": {
"self": "075a662c-b059-4af9-bb5f-8fe4633473c0/",
"trackDefinition": "#track_def_91",{
"playbackEventCollector":
"../../event/tracks/true/trackAsin/B00UMINC3W/objectId/0754af9-bb5f8fe46633473c0/duration/211000/trackContentEligibility/HAWKFIRE/"
}
then during playback, if the user instructs the app to stop playback 189,000 milliseconds into the track, the application must POST to the URI "../../event/tracks/true/trackAsin/B00UMINC3W/objectId/0754af9-bb5f-8fe46633473c0/duration/211000/trackContentEligibility/HAWKFIRE/"
with a POST-BODY of:
{
"trackPosition": 189000,
"reason": "userStop",
"wallClockTime": "2018-06-29T9:59:43",
"event": "stop"
}
PlaybackEventReport fields
Field name | Type | Description |
---|---|---|
event | string | The value should be either "start" or "stop". Use "start" when the track was not previously playing, but has now started. Use "stop" when the track was previously playing, but has now stopped. |
wallClockTime | timestamp | The actual time this event occurred. Should be reported using the time zone offset representing the local device time. |
trackPosition | number | The distance from the beginning of the track where this event occurred, in milliseconds. |
playbackDelay | number | The delay when starting or resuming the audio, in milliseconds. Should only be reported if it was observable by customer. Null if delay not applicable for this event. |
isDirectedPlay | boolean | True if the start or stop event was elicited by the user’s direct action, otherwise false. Null if actual value cannot be reported. Please file an Exception Request if you are unable to report this value. |
isShufflePlay | boolean | True if the user has a playable randomization feature enabled, otherwise false. Null if actual value cannot be reported. Please file an Exception Request if you are unable to report this value. |
cacheHitStatus | boolean | True if client is able to immediately start playback at the requested location in the song. False if not enough of track was already downloaded/cached to begin immediate playback. Null if actual value cannot be reported. Please file an Exception Request if you are unable to report this value. |
source | string | The source of the device’s internet connection, if available. Acceptable values: WIFI, WIRED, UNKNOWN, or the name of the cell carrier (e.g. "Verizon," "T-Mobile") |
transferSpeedBPS | number | A best guess toward the user's connection speed in bits per second. |
rebufferCount (stop only) | number | The number of times playback was interrupted to await further downloads or rebuffers during completion of the playable. |
reason | string | A code indicating the reason why this event occurred (see below). |
Start Event Reason Codes
userSelectTrack | This track started playing as a result of the user selecting a Playable whose trackDef member matches this track’s definition. |
userSelectPlayable | This track started playing as a result of the user selecting a Playable whose trackDef member does not match this track’s definition or is null or absent. |
autoAdvance | This track started playing automatically because the previous track in the track container finished playing. |
userAdvance | This track started playing because of a user action that advanced from the previous track in the track container. |
userAdvanceBackward | This track started playing because of a user action that advanced backward from the next track in the track container. |
userSeek | A user action resulted in a discontinuous jump in the playback position while playing the track. |
resume | Playback of this track resumed after having been paused. The trackPosition member should match the trackPosition member of the previous stop event. |
bufferUnderflow | Playback resumed after stopping due to the audio stream buffer running out. |
Stop Event Reason Codes
finish | This track played to completion. |
userAdvance | This track stopped playing because of a user action that advanced to the next track, typically pressing the next button. |
userAdvanceBackward | This track stopped playing because of a user action that advanced backward from the current track. This should be used when user presses back and either a different track is played, or this track is restarted. |
userPause | A user action resulted in playback stopping in a way that allows playback to resume from the same position. |
userStop | A user action resulted in playback stopping in a way that does not allow playback to resume from the same position. This includes user pressing stop or explicitly selecting a different track. |
userSeek | A user action resulted in a discontinuous jump in the playback position while playing the track (see below. |
userInactive | Playback stopped mid-track due to user not interacting with the device for too long or a power-saving feature. Includes both resumable and non-resumable states. |
systemPause | Playback stopped for any other reason not initiated by a user action (e.g. an incoming voice call or a power management event) in a way that allows playback to resume from the same position. |
systemStop | Playback stopped for any other reason not initiated by a user action in a way that does not allow playback to resume from the same position. |
error | Playback stopped for a reason that is indicative of unexpected conditions or system errors. |
bufferUnderflow | Playback stopped due to the audio stream buffer running out. |
Handling skip a track
The Prime experience has a skip limit feature. Every hour, a Prime user can skip a track 6 times before reaching a skip limit. To make the integration easier, there is a canSkip flag that tells if a user can skip a track. The canSkip flag is driven off of the events; whenever a user skips a track, we should receive a "Stop - userAdvance", if we do not receive the "userAdvance" event, the canSkip flag might never return false.
There are some scenarios where the user can or cannot skip backwards. The canSkipBackward flag tells if a user can or cannot skip backwards in behavior like Stations.
Below is a sample JSON partial response of a track that includes the canSkip and canSkipBackward flags:
"autoSkipOnError": false,
"duration": 207000,
"isrc": "GBAYE2100379",
"error": null,
"trackRating": null,
"trackTag": "B098Z4XHTT",
"isExplicit": "false",
"canSkip": true,
"canSkipBackward": true,
"album": {
"name": "Music Of The Spheres [Explicit]",
"indexWithinAlbum": 2,
"uri": "../../albums/B098YT7MP1/",
"asin": "B098YT7MP1"
},
Errors
It is important to check for errors after sending a play event report. In some cases, there can be issues which may generate an error message. For example: the end user is playing streams on multiple devices, but their subscription tier only allows them one stream.
In such a case, the client would receive an error message from the service. In this case, the client needs to follow the error, display any messages, and possibly stop playback on one or more devices.
See the section on errors for more information on General Error Reports.
Seeking Within A Track
Whenever a user action results in a discontinuous jump in the playback position within a track, clients should report a pair of playback events: a stop event at the previous playback position and a start event at the new playback position. The userSeek
reason code should be used for both events.
Such a discontinuous jump may sometimes require rebuffering of the audio stream at the new playback position. In that case, the start event should not be reported until the buffer is ready to resume playback.
Pausing
Whenever playback becomes paused, a stop
event with a proper reason code should be reported. Any events that happen while the track is in paused state should be reported as well, using the same PlaybackEventCollector.
Playback with DRM
DRM is currently required for any on device playback.
Users can not only play tracks, but in some cases they may rate them as well. Click here to learn more.