HTTP Live Streaming (HLS)

HTTP Live Streaming (HLS) is a streaming protocol developed by Apple. It enables web servers to stream audio and video over HTTP. HLS gives servers the ability to switch streams as network bandwidth changes. For more details, see:


Overview

Sonos supports two types of HLS: stream and track. Use HLS stream for streaming radio content. Use HLS track for programmed radio or on-demand content.

Sonos supports the following for HLS:

  • MIME/media types: application/x-mpegURL, application/vnd.apple.mpegurl, audio/mpegurl
  • Codecs and profiles: AAC-LC, HE-AAC, HEv2-AAC
  • Container format: MPEG-TS, MPEG-4
  • Playlist formats: m3u and m3u8
  • Sample rates: 48 kHz or lower

📘

Use the right itemtype

You must use the track itemType for HLS track or the stream itemType for HLS stream.

Not supported

  • Video. If the HLS stream or track has embedded video, the Sonos player attempts to find the audio stream. But audio playback may not work. This could also cause performance issues as the player would have to download the video as well.
  • Explicit tags on tracks within a stream. You can tag containers, so we recommend tagging any HLS stream that may play explicit content. See Tag & filter explicit content for details.
  • AAC SSR, AAC Main, and MP3 under HLS.

Players send the same reports for HLS stream and track formats, including a final playback report after 30 minutes of no playback activity to indicate that playback has stopped. See Add reporting for details.


Content encryption

You can send HLS segments unencrypted through HTTP or encrypted through HTTPS. Sonos also provides support for more encryption for HLS segments by using a content key. If you use a content key, the player will call getContentKey to request the key to decrypt the content. See Encrypt content for details.


Workflow

HLS uses playlists to stream content: the master playlist and the media playlist. The master playlist is optional and can point to many media playlists. Each media playlist supports a different bitrate. The Sonos player determines whether a URI is HLS by reading its media type. When the Sonos player receives a URI that it knows to be HLS, it reads the URI to see if it is a master playlist. If so, the player extracts and caches the list of media playlists and bitrates specified in it. If the initial playlist is a media playlist, the Sonos player caches it. It doesn't get bitrate information as there will only be a single bitrate with one media playlist.

If you have many streams, be sure to use one master playlist. For example, list all the URIs in one master playlist if:

  • Your service has streams with different bitrates.
  • You use multiple CDNs to host your content.

That way, if there is an error with a stream, the players have backup URIs that they can access.

Master playlists

A sample master playlist looks something like this:

#EXTM3U
#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=162481,BANDWIDTH=163306,CODECS="mp4a.40.2"
mid/RockAndRoll.m3u8
#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=322495,BANDWIDTH=324311,CODECS="mp4a.40.2"
high/RockAndRoll.m3u8

The above master playlist has two EXT-STREAM-INF blocks. These point to media playlists, each with a different bandwidth.

Media playlists

Media playlists contain the list of all available segments. For HLS track, this includes the entire list of segments. For HLS stream, it includes a sliding window of segments. Here's an example of a media playlist for HLS track:

#EXTM3U
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:10,
RockAndRoll320_0.aac
#EXTINF:10,
RockAndRoll320_1.aac
#EXTINF:10,
RockAndRoll320_2.aac
#EXTINF:10,
RockAndRoll320_3.aac
#EXTINF:10,
RockAndRoll320_4.aac
#EXTINF:10,
RockAndRoll320_5.aac
#EXTINF:10,
RockAndRoll320_6.aac
#EXTINF:10,
RockAndRoll320_7.aac
#EXTINF:10,
RockAndRoll320_8.aac
#EXTINF:10,
RockAndRoll320_9.aac
#EXTINF:10,
RockAndRoll320_10.aac
#EXTINF:10,
RockAndRoll320_11.aac
#EXTINF:10,
RockAndRoll320_12.aac
#EXTINF:10,
RockAndRoll320_13.aac
#EXTINF:10,
RockAndRoll320_14.aac
#EXTINF:2,
RockAndRoll320_15.aac
#EXT-X-ENDLIST

Adjusting bitrates with HLS

With HLS content, the master playlist points to multiple media playlists that each support a different bitrate. For Sonos players, this gives them the ability to change bitrates as necessary when streaming. Sonos players can make the following bitrate adjustments:

  • If the player can stream faster than it needs to maintain a buffer, it changes to a higher bitrate.
  • If the player can’t stream fast enough to maintain a buffer, it changes to a lower bitrate.

If the server providing the data is the same as the last one a player streamed from, it will use the same bit rate it ended up with before. If it is a different server, the player will start with the lowest bitrate available to play the content. The player will change to a higher bitrate if it can stream faster than needed to maintain a buffer.

How Sonos players retry when HLS tracks fail

If a Sonos player streams a non-HLS track and it cannot retrieve any audio from the URL, it will fail and try the next track in the queue. When players stream HLS content, they will look for a source that returns content in the first 10 seconds. If the player doesn’t find a playable segment in that time, it will fail and move on to the next track in the Sonos queue.

Once a player plays a single sample, it uses the amount of buffer available to decide what to do when a playlist or track fails. If the player has less than 10 seconds of audio buffered, it switches to a different data source if it has one and refreshes the master playlist if it doesn’t. If the player has more than 10 seconds of audio buffered, it continues to try the current data source until it has less than 10 seconds buffered. The player fails the track or stream when it runs out of buffer.


HLS track example

The player sends a getMetadata request to your service for a container with HLS track content:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://www.sonos.com/Services/1.1">
   <soapenv:Header>
   <ns:credentials>
    ...
      </ns:credentials>
   </soapenv:Header>
   <soapenv:Body>
      <ns:getMetadata>
         <ns:id>al:42</ns:id>
         <ns:index>0</ns:index>
         <ns:count>20</ns:count>
      </ns:getMetadata>
   </soapenv:Body>
</soapenv:Envelope>

Your service responds with the tracks that are available to play. The Sonos app displays these to the user. Note that the itemType is track and the mimeType is audio/mpegURL.

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://www.sonos.com/Services/1.1">
   <s:Body>
      <ns:getMetadataResponse>
         <ns:getMetadataResult>
            <ns:index>0</ns:index>
            <ns:count>14</ns:count>
            <ns:total>14</ns:total>
            <ns:mediaMetadata>
               <ns:id>tr:484</ns:id>
               <ns:itemType>track</ns:itemType>
               <ns:title>AudioSample</ns:title>
               <ns:isFavorite>false</ns:isFavorite>
               <ns:mimeType>audio/mpegURL</ns:mimeType>
               <ns:trackMetadata>
                  <ns:artistId>ar:32</ns:artistId>
                  <ns:artist>Sample</ns:artist>
                  <ns:albumId>al:42</ns:albumId>
                  <ns:album>On-demand HLS</ns:album>
                  <ns:duration>321</ns:duration>
                  <ns:albumArtURI>https://test.example.com/assets/images/test.jpg</ns:albumArtURI>
                  <ns:canPlay>true</ns:canPlay>
                  <ns:canAddToFavorites>true</ns:canAddToFavorites>
                  <ns:canResume>false</ns:canResume>
               </ns:trackMetadata>
            </ns:mediaMetadata>
            <ns:mediaMetadata>
               <ns:id>tr:485</ns:id>
               <ns:itemType>track</ns:itemType>
               <ns:title>AudioSample2</ns:title>
               <ns:isFavorite>false</ns:isFavorite>
               <ns:mimeType>application/vnd.apple.mpegURL</ns:mimeType>
               <ns:trackMetadata>
                  <ns:artistId>ar:32</ns:artistId>
                  <ns:artist>Sample</ns:artist>
                  <ns:albumId>al:42</ns:albumId>
                  <ns:album>On-demand HLS</ns:album>
                  <ns:duration>220</ns:duration>
                  <ns:albumArtURI>https://test.example.com/assets/images/test.jpg</ns:albumArtURI>
                  <ns:canPlay>true</ns:canPlay>
                  <ns:canAddToFavorites>true</ns:canAddToFavorites>
                  <ns:canResume>false</ns:canResume>
               </ns:trackMetadata>
            </ns:mediaMetadata>
             ...
         </ns:getMetadataResult>
      </ns:getMetadataResponse>
   </s:Body>
</s:Envelope>

📘

Enabling scrub and skip

Note that duration is required if you want to enable the user to scrub or skip the track. See getMetadata for details.

The user chooses to play a track. The player sends a getMediaURI request:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://www.sonos.com/Services/1.1">
   <s:Header>
   <ns:credentials>
    ...
      </ns:credentials>
   </s:Header>
   <s:Body>
      <ns:getMediaURI>
         <ns:id>tr:484</ns:id>
      </ns:getMediaURI>
   </s:Body>
</s:Envelope>

Your service sends a getMediaURI response. It includes a getMediaURIResult value for the URI to the audio index file. This is the HLS playlist file:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://www.sonos.com/Services/1.1">
   <s:Body>
      <ns:getMediaURIResponse>
         <ns:getMediaURIResult>http://static.example.com/assets/hls/prog_index.m3u8</ns:getMediaURIResult>
         <ns:deviceSessionToken>1234567890123_4567890123456_00-00-00-00-00-00:A</ns:deviceSessionToken>
      </ns:getMediaURIResponse>
   </s:Body>
</s:Envelope>

The player caches the URLs to the media in the index file and requests them when needed.


HLS stream example

The player sends a getMetadata request to your service for a container with HLS stream content:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://www.sonos.com/Services/1.1">
   <soapenv:Header>
   <ns:credentials>
    ...
      </ns:credentials>
   </soapenv:Header>
   <soapenv:Body>
      <ns:getMetadata>
         <ns:id>browse:radio</ns:id>
         <ns:index>0</ns:index>
         <ns:count>50</ns:count>
      </ns:getMetadata>
   </soapenv:Body>
</soapenv:Envelope>

Your service responds with the streams that are available. Note that the itemType is stream and the mimeType is application/x-mpegURL.

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://www.sonos.com/Services/1.1">
   <s:Body>
      <ns:getMetadataResponse>
         <ns:getMetadataResult>
            <ns:index>0</ns:index>
            <ns:count>6</ns:count>
            <ns:total>16</ns:total>
             ...
            <ns:mediaMetadata>
               <ns:id>shls:4</ns:id>
               <ns:itemType>stream</ns:itemType>
               <ns:title>StreamingRadio1</ns:title>
               <ns:mimeType>application/x-mpegURL</ns:mimeType>
               <ns:streamMetadata/>
            </ns:mediaMetadata>
            <ns:mediaMetadata>
               <ns:id>shls:5</ns:id>
               <ns:itemType>stream</ns:itemType>
               <ns:title>StreamingRadio2</ns:title>
               <ns:mimeType>application/x-mpegURL</ns:mimeType>
               <ns:streamMetadata/>
            </ns:mediaMetadata>
             ...
         </ns:getMetadataResult>
      </ns:getMetadataResponse>
   </s:Body>
</s:Envelope>

The user chooses to play a stream. The player sends a getMediaURI request to your service:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://www.sonos.com/Services/1.1">
   <soapenv:Header>
   <ns:credentials>
   ...
      </ns:credentials>
   </soapenv:Header>
   <soapenv:Body>
      <ns:getMediaURI>
         <ns:id>shls:5</ns:id>
      </ns:getMediaURI>
   </soapenv:Body>
</soapenv:Envelope>

Your service sends a getMediaURI response with the URI to the audio index file. This is the HLS playlist file:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://www.sonos.com/Services/1.1">
   <s:Body>
      <ns:getMediaURIResponse>
         <ns:getMediaURIResult>http://radio.example.com/radio/folder/java/master.m3u8</ns:getMediaURIResult>
      </ns:getMediaURIResponse>
   </s:Body>
</s:Envelope>

The player caches the URLs to the media in the index file and requests them when needed.