Firmware Integration Guide

This page will guide you through integrating Audiogum services to your firmware.

Please also see docs for our Embedded SDK - libaudiogum.

Background and Assumptions

The following base assumptions and knowledge are required to start integration.

1) Firmware exists that offers local network and Internet connectivity. Optionally, the firmware may already offer local network APIs to control playback such as adjust volume, skip, etc.

2) The firmware has either has an existing HTTP / HTTPS streaming media player OR a suitable library can be used to provide the feature – e.g. https://www.musicpd.org/, https://wiki.archlinux.org/index.php/Mplayer, https://github.com/GStreamer/gstreamer or https://freedesktop.org/software/pulseaudio/doxygen/index.html.

3) The audience have a familiarity with Internet protocols - specifically HTTP and OAuth2. See https://oauth.net/2/ for a background on OAuth2.

An example of a C firmware implementation covering playback and voice control is available at https://github.com/audiogum/ag-fw.

More detail on Audiogum Platform features referred to on this page are available on the Content Aggregation, User Accounts, Playback and Analytics pages.


Playback - Introduction

The Audiogum Platform abstracts playback of playlists, albums, audiobooks and other playable audio from various services behind a simple set of APIs. We call this concept “playables”.

A ”playable” is the piece of data describing the parameters needed for the client to play something and potentially some state associated with playing it.

We offer this abstraction for these reasons:

1) to simplify firmware integration with services - rather than dealing with every different service you wish to integrate, handling different API authentication schemes, breaking changes and different response formats, Audiogum offer a consistent API set that returns lists of items to play.

2) to cope with different types of content - not just playlists created by a user or service, but also to allow dynamic personal playlists that can play on any service your customer has a subscription to. In some countries, it is possible to play a playlist that sources media from multiple content services.

3) adding new services requires minimal change to your apps and firmware (typically none). With the config-driven approach, new services can light up without new releases (providing the service does not require an SDK for playback).

Examples of playables:

  • A specific playlist from a specific service (e.g. Tidal)
  • An internet radio station
  • An artist-seeded Audiogum dynamic playlist (e.g. played via Napster)
  • An Audiogum personalised dynamic playlist (e.g. played via Tidal)
  • A voice-seeded Audiogum dynamic playlist

Advanced scenarios include use of an content provider SDK to handle playback of a track that are not covered in this guide.

Flow Overview

Fundamentally, playback is a continuous loop of:

  • Fetch details of what to play
  • Play track(s)
  • Report Analytics

Playing content

More details can be found on the Playback page.


Flow Details

This section details each of the API calls in the sequence diagram above showing the libaudiogum approach and the raw REST API calls.

Authentication

With libaudiogum, authentication is handled automatically for you once the library has been initialized.

ag_init("my_client_id", "my_client_secret", "ubuntu", "14.1", "mydevice", "0.1", "nativetestdeviceid");

To use the Audiogum API, you will need an access_token for the device. This is obtained by requesting a token with grant_type=client_credentials:

POST /v1/tokens
Content-Type: application/json
Authorization: Basic Y2xpZW50aWQ6Y2xpZW50c2VjcmV0 // Base64(client_id+":"+client_secret)
{
  "grant_type": "client_credentials",
  "scope": "device-all",
  "deviceid": "78:4f:43:5b:0f:d3" // consistent device id – e.g. mac address
}

Response:

{
  "access_token": "v1::Izfus79f4ir9Vmw63wgJAQ==::tFEs...",
  "token_type": "bearer",
  "expires_in": 86400
}

The accces_token is then used in the authorization header in later calls.

Get Playable

To get the next tracks to play, you just need the ID of the playable being played. As mentioned above, this could be initiated by a controller app or hardware buttons.

Different response types and the implications are covered in detail on the Playback page.

ag_playable_items *playlist_items = ag_get_playable_items("b28a6a5570d84b76882d1f43282951bf");

GET /v1/playables/{id}
Authorization: Bearer {access_token}

Example Response showing an HTTPS stream with display metadata removed for clarity:

{
 "userid": "c0cce16a3952487db2cfc08a93edb9e1",
 "itemsperpage": 1,
"startindex": 0,
 "total": 12, 
 "items": [
   {
     "service": "napster",
     "index": 0,
     "ref": "tra.54624359",
     "type": "song",
     "playbackcodec": "AAC",
     "streamurl": "https://xxxxxxxxxxx"
   }],
 "parameters": { "ref": "alb.54624358", "service": "napster", "type": "album" },
 "services": {
   "napster": {
     "userid": "xxxx",
     "playbackprotocol": "https",
     "timelimited": 4440
   }
 }
} 

Report Playback

Upon completing, stopping or skipping the item, analytics should be captured. See Play Analytics for details of what data to send. We recommend batching analytics events into groups of around 10 to save processing / network interaction.

More details on what events to send and when can be found on the Analytics page.

libaudiogum provides a structure to store analytics events that can then be batched and sent. Simply allocate memory, populate the event with the details required and at a later stage send the events and free the memory.

int num_events = 0;
ag_event *events = NULL;

// song started
num_events++;
events = realloc(events, num_events * sizeof(ag_event));
ag_populate_play_event(&events[num_events - 1], AG_EVENT_ACTION_BEGIN, AG_EVENT_CONNECTION_WAN, playlist_items->analytics_json, 0, &playlist_items->items[0], 0);

// send batch
ag_response *event_resp = ag_send_events(events, num_events);

POST /v1/events
Authorization: Bearer {access_token}
{
   "events" : [{
      "action" : "start",
      "client" : "myclient",
      "clientversion" : "1234",
      "connection" : "wan",
      "countrycode" : "gb",
      "devicemanufacturer" : "audiogum",
      "devicename" : "test-device",
      "localtime" : "2017-05-21T10:22:55.000",
      "platform" : "Android",
      "platformversion" : "1234",
      "playable" : {
         "id" : "b3192fba407043cfb022dd0b1cf19920",
         "name" : "BBC Radio 1",
         "parameters" : {
            "ref" : "1357",
            "service" : "vtuner",
            "type" : "internetradio"
         },
         "userid" : "b8e0160f6267476385175f40874db3e8"
      },
      "presetnumber" : 3,
      "sequencenumber" : 1,
      "sessionid" : "9f4d25a1c9074bbd9b392d5f4c64cec8",
      "source" : "playable",
      "type" : "playevent"
   }],
   "schemaversion" : "1.0"
}

Refresh Time-limited Stream URLs

Playables from services that are timelimited return streamurls that can expire.

libaudiogum calculates a stream_uri_expiry in the ag_playable_item structure and includes a helper function to work out when to refresh a stream URL.

The Refresh Stream URI function takes the item to refresh and updates the stream_uri and stream_uri_expiry fields.

ag_response *refresh_resp = ag_refresh_playable_stream_uri(playlist_items->id, &playlist_items->items[0]);

The firmware will need to keep track of the time it received the last set of items to play and also note the value of the service's timelimited to check if the item is expected to play. We recommend refreshing the streamurls when the current time is within a minute of (receive_time + timelimited).

POST /v1/events
Authorization: Bearer {access_token}
POST /v1/playables/{id}/streamurls/{service}
{
 "items": [
  {"ref": "46890235"},
  {"ref": "10220880"}]
}


Test Data

For integration purposes, Audiogum will soon offer a set of specific playables that can be used for developing against.

In the meantime, the following APIs can be used to create a playable for your testing. You can use the Audiogum API Explorer to easily call these APIs.

1) Create a user account: POST /v1/user – or register from the API explorer. This API returns an OAuth2 access_token to call APIs on behalf of the user. Once you have a user, you login with POST /v1/tokens.

2) Store Audio Service credentials for the user with GET /v1/user/{service}/authorize to start the login flow and POST /v1/user/{service}/token to store the access details.

3) Create a Playable: GET /v1/content to search for content and POST /v1/user/playables to create - this will return the new Playable ID (or an existing one if the same parameters have already been used). Note that as part of this, you will need to specify a deviceid that must match the one used.