Step 5: Build Your Web Player (VSK Echo Show)
In this step, you will build your own web player. The web player will reference and initialize the Alexa JavaScript library and provide other functionality.
- Web player requirements
- Build your own web player
- Migrate or build your web player
- Style your web player
- Include the Alexa Video JavaScript library
- Initialize the Alexa Video JavaScript library
- Register event handlers
- Implement handlers
- Hide loading overlay
- Send playback lifecycle events to Alexa
- Configure allowed operations
- Send content metadata to Alexa
- Get and set the state of closed captions
- Report fatal errors
- Create ending experience
- Host the web player on a publicly accessible URL
- Next steps
Web player requirements
As you build your own web player, keep in mind these requirements around codecs, formats, and standards:
Video requirements:
- HLS/MPEG-DASH
- MP4 H.264
- Widevine DRM Level 1
- Encrypted Media Extensions (EME)
- Media Source Extensions (MSE)
Audio requirements:
- MP4 with AAC
- WebM with Vorbis
- WebM with Opus
- AAC-LC is supported, but AAC-SBR is not. Media should follow the audio specifications defined for "chromium" to ensure proper playback. More information on chromium can be found in Audio/Video in the Chromium Projects site.
Screen resolution requirements
Video content sent to Amazon devices cannot be of a higher resolution than the display’s resolution. Check the display’s resolution in your web app with the following:
var displayHeight = window.screen.height * window.devicePixelRatio;
var displayWidth = window.screen.width * window.devicePixelRatio;
Your web player must select a video resolution less than or equal to the height and width calculated above. If your video resolution is greater than the display’s resolution, the device may experience dropped frames, lagging, or sometimes not play your video content at all.
The user agent string (window.navigator.userAgent
) will always be prepended with AlexaWebMediaPlayer
.
- Example of a user agent string:
AlexaWebMediaPlayer/1.0 (Linux; Android 7.1.2)
If any of the requirements above are different from your existing stream-picking logic, use the user agent string to check if you are running on an Echo Show device. If you are, use the above guidance.
Build your own web player
To build your own web player, complete the high-level tasks described in each of the following sections.
Migrate or build your web player
If you have existing web assets that deliver your content, isolate the web player component and supporting functionality such as metrics reporting, advertisement logic, and other dependencies. Your video must play exclusively in full-screen mode. Links to any other website are restricted.
Style your web player
Style your web player to apply the visual controls and non-video elements of your player. For certification guidelines related to the video experience, see Test for Certification.
Include the Alexa Video JavaScript library
The alexa-web-player-controller JavaScript library provides a communication bridge between Alexa and your web player. Load the JavaScript library with the following script tag in your HTML:
<script type="text/javascript" src="https://dmx0zb087qvjm.cloudfront.net/alexa-web-player-controller.0.1.min.js"></script>
Initialize the Alexa Video JavaScript library
In your web app's initialization code, wait for the Alexa object to be ready, then initialize the alexa-web-player-controller library with your readyCallback
and errorCallback
. The readyCallback
is invoked when the library is ready for execution.
In the readyCallback
, you receive a controller object to communicate with the library, which includes the control methods. The errorCallback
is invoked if there is an error that causes the web player to close. The user receives a human-readable error message.
The alexa-web-player-controller library closes the web container after sending the error message. During initialization of the library, you may also optionally register event handlers by adding an argument containing a map of callback functions keyed by event names. Otherwise you can register handlers using the controller.on(handlers)
/ controller.on(event, handler)
method explained in the next section. The handlers play, pause, and resume are required.
If your player supports closed captions, call the getClosedCaptionsState
method during initialization. The initialization sequence is considered complete when the setPlayerState
method is called with state IDLE
. More information about the getClosedCaptionsState
and setPlayerState
methods is provided in the following sections.
// Load Alexa Video JavaScript library before this script.
AlexaWebPlayerController.initialize(readyCallback, errorCallback);
or
AlexaWebPlayerController.initialize(readyCallback, errorCallback, handlers);
Register event handlers
Register handlers using the controller.on(handlers)
/controller.on(event, handler)
method. The handlers play, pause, and resume are required.
function readyCallback(controller) {
var Event = AlexaWebPlayerController.Event;
var handlers = {
Event.LOAD_CONTENT: function handleLoad(params) {},
Event.PAUSE: function handlePause() {},
Event.RESUME: function handleResume() {},
Event.ADJUST_SEEK_POSITION: function handleAdjustPos(offsetInMilliseconds) {},
Event.NEXT: function handleNext() {},
Event.PREVIOUS: function handlePrevious() {},
Event.CLOSED_CAPTIONS_STATE_CHANGE: function handleCCState(state) {},
Event.PREPARE_FOR_CLOSE: function handlePrepareForClose() {},
Event.ACCESS_TOKEN_CHANGE: function handleAccessToken(accessToken) {}
};
controller.on(handlers);
}
The following table describes the various handlers and their parameters.
Key | Key Desc. | Handler Parameter | Type | Param. Desc. |
---|---|---|---|---|
LOAD_CONTENT |
Load a given piece of content. | params.contentUri |
string | URI of content provided in Video Skill API response. |
params.accessToken |
string | User credentials | ||
params.offsetInMilliseconds |
integer | Offset from beginning of content to begin playback. | ||
params.autoplay |
boolean | Flag to automatically play content after loading. We expect the player to initiate playback when autoplay=true and to load into a PAUSED state when autoplay=false . |
||
PAUSE |
Pause playback | none | — | — |
RESUME |
Resume playback | none | — | — |
ADJUST_SEEK_POSITION |
Change playback position by an offset from the current position. | offsetInMilliseconds |
integer | If positive, offset from start. If negative, offset from end. |
NEXT |
Advance to the next video, if available. | none | — | — |
PREVIOUS |
Go back to the previous video, if available. | none | — | — |
CLOSED_CAPTIONS_STATE_CHANGE |
Update closed captions state | state | object | Closed captions state including enabled, text, background, window background. |
PREPARE_FOR_CLOSE |
Prepare for the device to close the web container in 250 ms, and handle any remaining actions | none | — | — |
ACCESS_TOKEN_CHANGE |
Update access token | accessToken |
string | User credentials |
Implement handlers
How you handle commands depends on the implementation of your player. All handlers must return a Promise object to be resolved on successful handling of the command or rejected with an error object with errorType
and message if there is a failure. If a failure occurs for PLAY
, PAUSE
or RESUME
, the device calls PREPARE_FOR_CLOSE
and tries to close the web player.
For the LOAD_CONTENT
operation, your handler receives parameters including contentUri
sent in your AWS Lambda function response representing the content to be played. If your service requires the user be authenticated to stream content, an accessToken
is included as well. Additional details may also be included, such as offsetInMilliseconds
if the user is picking up in the middle of content, or autoplay
if the content needs to auto play after loading. The handler returns a Promise object to be resolved on successful handling of the play command or rejected with an error object with errorType
and message if there is a failure.
Hide loading overlay
Your player is not visible until controller.showLoadingOverlay
is called with a value of false. Make the call after loading your assets are loaded and the experience is ready to show. You must call this method to disable the overlay when the content is loaded and UI presentable. The loading overlay is always shown during initialization.
Subsequent calls to load content (such as calls for new, unrelated content) allow you to turn on the loading overlay and turn it off appropriately, but is not required. If you want a custom loading screen, you can disable this before content is ready and provide your own visualizations. This must not be called when content is playing.
controller.showLoadingOverlay(false);
Send playback lifecycle events to Alexa
When your player changes state, use the controller.setPlayerState(playerState)
method to pass along the lifecycle events to Alexa. The playerState
includes two properties, State
and positionInMilliseconds
.
Call the controller.setPlayerState(playerState)
method any time the player state changes.
The playerState
needs to match the current content playback behavior so Alexa doesn’t close the web container during playback or persist the container for too long when playback is paused or stalled.
controller.setPlayerState({
state: AlexaWebPlayerController.State.IDLE,
positionInMilliseconds: 0
});
The following table lists the player states.
State | Description |
---|---|
IDLE |
Player is idle; no content is loaded or playing. Player is ready to stream content. |
BUFFERING |
Playback is suspended due to content buffering. |
PLAYING |
Player is actively streaming content. |
PAUSED |
Playback is suspended during content. |
Configure allowed operations
When the allowed operations for Alexa change, set the allowed operations by using controller.setAllowedOperations(allowedOperatons)
through the JavaScript library. Allowed operations require handlers, which are not pre-implemented. By default, an operation is not allowed until the handler has been implemented and the operation is set to true in allowedOperations
.
controller.setAllowedOperations({
adjustRelativeSeekPositionForward: true,
adjustRelativeSeekPositionBackwards: true,
setAbsoluteSeekPositionForward: true,
setAbsoluteSeekPositionBackwards: true,
next: true,
previous: true,
});
Name | Type | Prerequisite Handler | Description |
---|---|---|---|
allowedOperations |
object | N/A | Allowed operations for content currently in the player. |
adjustRelativeSeekPositionForward |
boolean | adjustSeekPosition |
If true, allow user to seek forward relative to the current position. |
adjustRelativeSeekPositionBackwards |
boolean | adjustSeekPosition |
If true, allow user to seek backwards relative to the current position. |
setAbsoluteSeekPositionForward |
boolean | setSeekPosition |
If true, allow user to seek forward to an absolute position. |
setAbsoluteSeekPositionBackwards |
boolean | setSeekPosition |
If true, allow user to seek backwards to an absolute position. |
next |
boolean | next |
If true, allow user to request the next content in the play queue. |
previous |
boolean | previous |
If true, allow the user to request the previous content in the play queue. |
Send content metadata to Alexa
Pass along metadata for the current content in the controller.setMetadata(metadata)
method. Do this for both the initial content and any new content played thereafter.
controller.setMetadata({
type: AlexaWebPlayerController.ContentType.TV_SERIES_EPISODE,
value: {
name: "name",
closedCaptions: {
available: true
},
durationInMilliseconds: 1000,
series: {
name: "name",
seasonNumber: 1
},
episode: {
number: 1,
name: "name"
}
}
});
Metadata for other video:
controller.setMetadata({
type: AlexaWebPlayerController.ContentType.VIDEO,
value: {
name: "",
closedCaptions: {
available: true
},
durationInMilliseconds: 1000,
}
});
Name | Description | Req'd | Type | Values |
---|---|---|---|---|
type |
Content type of the metadata. | Yes | String | AlexaWebPlayerController.ContentType |
value |
Value of the metadata. Each type may have a different set of values. | Yes | Object | JSON object |
name |
Name of the video. | Yes | String | Example: Interstellar |
closedCaptions |
Closed captions of the video. | Yes | Object | JSON object |
available |
Availability of the closed captions. | Yes | Boolean | true , false |
durationInMilliseconds |
Duration of the video in milliseconds. | No | Number | Example: 3141343 |
series |
Metadata of a series. | No | Object | JSON Object |
name |
Name of a series. | No | String | Example: Survivor: Borneo |
seasonNumber |
Number of the season. | No | String | Example: 1 |
episode |
Metadata of an episode. | No | Object | JSON Object |
name |
Name of the episode. | No | String | Example: The Marooning |
number |
Number of the episode. | No | String | Example: 1 |
AlexaWebPlayerController .ContentType |
Value (string) | Description |
---|---|---|
TV_SERIES_EPISODE |
TV_SERIES_EPISODE |
Content type for TV series episode |
VIDEO |
VIDEO |
Content type for video |
Get and set the state of closed captions
Retrieve the device-level setting for closed captions at the beginning of playback by using the controller.getClosedCaptionsState()
method. To toggle closed captions on and off, use the controller.setClosedCaptionsStateEnabled(enabled)
method.
controller.getClosedCaptionsState();
controller.setClosedCaptionsStateEnabled(isEnabled: boolean);
Closed captions state:
{
enabled: ,
text: {
size: ,
color: ,
opacity: ,
font: ,
edge: ,
},
background: {
color: ,
opacity: ,
},
windowBackground: {
color: ,
opacity: ,
}
}
Name | Description | Type | Values |
---|---|---|---|
enabled |
Whether the closed captions are enabled | Boolean | true , false |
text |
Text preference | Object | N/A |
size |
Size of the text | Number |
Text Size (in pixels) Example: 10 |
color |
Color of the text | String |
Text Color (RGB value in HEX code) Example: #ff0000
|
opacity |
Color opacity of the text | Number |
Text Color Opacity (alpha value between 0 - 1.0) Example: 1.0
|
font |
Font of the text. | String |
Font (from Google Fonts)
|
edge |
Edge style of the text. | Number |
Edge Style:
|
background |
Text background preference. | Object | N/A |
color |
Color of the text background. | String |
Text Background color(This is the RGB value of the color) Example: #ff0000
|
opacity |
Opacity of the text background | Number |
Text Background Opacity* Percentage (disabled when Text Background Color set to default; alpha value between 0 - 1.0) Example: 1.0
|
windowBackground |
Window background preferences | Object | N/A |
color |
Color of the closed captions window background. | String |
Window Background color (RGB value) Example: #ff0000
|
opacity |
Opacity of the closed captions window background. | Number |
Window Background Opacity (disabled when Window Background Color set to default; alpha value between 0 - 1.0) Example: 1.0
|
Report fatal errors
If your player encounters an error and is unable to play content, use the controller.sendError(error)
method to send a fatal error to Alexa. Alexa hides the web app, call the PREPARE_FOR_CLOSE
handler and close the web player. For non-fatal errors, there is no need to send back to Alexa.
controller.sendError({
type: AlexaWebPlayerController.ErrorType.PLAYER_ERROR,
message: 'Error message as string'
});
Error Type | Description |
---|---|
PLAYER_ERROR |
Send PLAYER_ERROR event when there is an unrecoverable error having to do with the media player. |
CLIENT_ERROR |
For any client side error not related to the player, send an CLIENT_ERROR . |
SERVER_ERROR |
SERVER_ERROR indicates an error occurred in server side including failed requests, unable to buffer content, unreachable assets, and any connectivity issues. |
Create ending experience
When you think the play session has ended, call controller.close()
method to inform Alexa. Alexa then hides the web app, calls the PREPARE_FOR_CLOSE
handler, and ends the experience.
controller.close();
Host the web player on a publicly accessible URL
Finally, make your player available on a public URL. Make sure your web player can be accessed by using HTTPS, because Alexa devices require a secure connection.
At this point, your web player is almost ready to be tested on a multimodal device. But first, you must implement the required Video Skill APIs in your skill's Lambda function as described in the next section, Step 6: Respond to Alexa Directives Delivered to Lambda.
Next steps
Go on to Step 6: Respond to Alexa Directives Delivered to Lambda.
Last updated: Jan 04, 2023