SC playlist¶
Note
The single-controller (SC) playlist API is experimental.
The single-controller (SC) playlist is built for smooth, continuous
back-to-back playback of multiple items. ScPlaylist
inherits from Player, so every method defined there — Player.on,
Player.off, Player.getPosition, Player.seek,
and more — also works on the playlist instance.
Create the playlist¶
Instantiate the playlist the same way you’d instantiate a Player:
<video id="my_video"></video>
const playlist = new clpp.playlist.ScPlaylist("my_video", {
license: "<your_license_key>",
viewerId: "<user_id>",
});
Load content¶
Load a playlist of two items with ScPlaylist.openPlaylist.
Playback starts from the first item. Each item must have a unique id:
await playlist.openPlaylist(0, [
{
id: "ASSET1",
source: { url: "..." },
},
{
id: "ASSET2",
source: { url: "..." },
},
]);
Playback-related options such as autoplay, startTime, and streaming
must be passed either through the constructor or as properties of the first
item passed to openPlaylist():
await playlist.openPlaylist(0, [
{
id: "ASSET1",
source: { url: "..." },
autoplay: true,
playlist: {
startTime: 120,
},
},
{
id: "ASSET2",
source: { url: "..." },
},
]);
Encrypted content is supported, but only when every item is encrypted with
the same method and key. Pass the drm configuration as a property of the
first item:
await playlist.openPlaylist(0, [
{
id: "ASSET1",
source: { url: "..." },
drm: { /* ... */ },
},
{
id: "ASSET2",
source: { url: "..." },
},
]);
Use playlist.startTime to set the position playback starts from. The
position must fall inside the first item’s timeline. It defaults to 0 and
doesn’t affect the timeline’s duration:
await playlist.openPlaylist(0, [
{
id: "ASSET1",
source: { url: "..." },
playlist: {
startTime: 120, // start 120 seconds in
},
},
]);
Use playlist.endTime to set the position playback stops at. The value is
relative to the start of the first item and can span any number of items. It
doesn’t affect the timeline’s duration:
await playlist.openPlaylist(0, [
{
id: "ASSET1",
source: { url: "..." },
playlist: {
endTime: 600, // end at 600 seconds
},
},
]);
Append more items with ScPlaylist.addItem:
await playlist.addItem({
id: "ASSET3",
source: { url: "..." },
});
Seeking¶
Call Player.seek to seek within the currently playing item:
// Seek to position 2 minutes inside the current item
await playlist.seek(120);
Call ScPlaylist.seekWith to seek relative to the current
position — the seek can cross into a following item, or a preceding one if
the value is negative:
// Seek 2 minutes forward, possibly into one of the next items
await playlist.seekWith(120);
// Seek 2 minutes backward, possibly into one of the preceding items
await playlist.seekWith(-120);
Timeline windows¶
Call ScPlaylist.getTimeline to get a representation of
the playlist timeline. Each playlist item is represented by one window, and
the currently playing item is the current window:
const timeline = playlist.getTimeline();
// Get the index of the currently playing/selected item
const activeIndex = timeline.getCurrentWindowIndex();
const windows = timeline.getWindows();
windows.forEach((win, index) => {
win.getStart();
win.getDuration();
// window ID is equal to configuration.id of the corresponding playlist item
win.getId();
});
Timeline modes¶
The playlist.timeline configuration option switches to flat mode. In
flat mode, the timelines of individual playlist items join into a single
continuous timeline:
const playlist = new clpp.playlist.ScPlaylist("video", {
license: "<your_license_key>",
viewerId: "<user_id>",
playlist: {
timeline: "flat", // switch to flat timeline mode
},
});
Switching to flat mode changes the behavior of timeline-related methods
such as Player.getPosition, Player.getDuration,
Player.getSeekRange, and Player.seek.
Timeline cropping¶
Use playlist.cropTimeline to crop the timeline according to the values of
playlist.startTime and playlist.endTime. Cropping affects the duration of
the timeline.
Crop off the first 120 seconds of the first item and start playback from the new position 0:
await playlist.openPlaylist(0, [
{
id: "ASSET1",
source: { url: "..." },
playlist: {
startTime: 120,
cropTimeline: true,
},
},
]);
Crop off the last 200 seconds of the last item — that is, crop anything past a cumulative 600 seconds from the start of the first item:
await playlist.openPlaylist(0, [
{
id: "ASSET1", // duration 400
source: { url: "..." },
playlist: {
endTime: 600,
cropTimeline: true,
},
},
{
id: "ASSET2", // duration 400
source: { url: "..." },
},
]);
Crop off the first 200 seconds of the first item and the last 200 seconds of the second (last) item:
await playlist.openPlaylist(0, [
{
id: "ASSET1", // duration 400
source: { url: "..." },
playlist: {
startTime: 200,
endTime: 600,
cropTimeline: true,
},
},
{
id: "ASSET2", // duration 400
source: { url: "..." },
},
]);
Timeline cues¶
Listen for timeline cues defined by DASH EventStream events or HLS
EXT-X-DATERANGE tags:
playlist.on(clpp.events.TIMELINE_CUE_ADDED, (e) => {
// The cue appeared in the manifest
const cue = e.detail;
});
playlist.on(clpp.events.TIMELINE_CUE_ENTER, (e) => {
// Playback position entered the cue
const cue = e.detail;
});
playlist.on(clpp.events.TIMELINE_CUE_EXIT, (e) => {
// Playback position exited the cue
const cue = e.detail;
});
Each item’s cues are parsed as soon as the item is added to the playlist, so
you get an overview of the cues of every item, not just the currently playing
one. Call Player.getTimelineCues for a list of all cues across all
items:
const cues = playlist.getTimelineCues();
Each cue can be matched to the item it belongs to via the configuration ID:
cue.configurationId === configuration.id;
In basic timeline mode, cues are positioned relative to the start of the
item they belong to:
// [ASSET1, ASSET2]
cue.configurationID; // ASSET2
cue.startTime === 10; // starts 10 seconds into ASSET2
cue.endTime === 20; // ends 20 seconds into ASSET2
In flat timeline mode, cues are positioned relative to the start of the
first item of the playlist:
// [ASSET1, ASSET2]
// ASSET1 has a duration of 400 seconds
cue.configurationID; // ASSET2
cue.startTime === 410; // starts 10 seconds into ASSET2
cue.endTime === 420; // ends 20 seconds into ASSET2
Stopping playback¶
Call ScPlaylist.release to stop playback and reset the
playlist to its initial state:
await playlist.release();
Events¶
The playlist emits events for the currently playing item and the timeline.
Events specific to the single-controller playlist are prefixed
SC_PLAYLIST.
SC_PLAYLIST_TIMELINE_CHANGED¶
Triggered whenever a new window is added to the timeline, or when a timeline window changes duration or turns static for a live stream. Depending on your UI, this may be a good time to redraw the timeline:
function handler() {
const duration = playlist.getDuration();
const position = playlist.getPosition();
// redraw the timeline
}
playlist.on(clpp.events.SC_PLAYLIST_TIMELINE_CHANGED, handler);
// playlist.off(clpp.events.SC_PLAYLIST_TIMELINE_CHANGED, handler);
SC_PLAYLIST_ITEM_CHANGED¶
Triggered whenever playback transitions from one item to another. The
event’s id property corresponds to the id property of the item’s
configuration — another good point to redraw your UI timeline:
function handler(event) {
// event.id contains the `id` property of the current item (e.g. "ASSET1")
console.log("Item changed", event.id);
const duration = playlist.getDuration();
const position = playlist.getPosition();
// redraw the timeline
}
playlist.on(clpp.events.SC_PLAYLIST_ITEM_CHANGED, handler);
// playlist.off(clpp.events.SC_PLAYLIST_ITEM_CHANGED, handler);
SC_PLAYLIST_ENDED¶
Triggered when playback reaches the end of the playlist. The end of the
playlist is either the endTime configuration or, by default, the end of
the last item:
function handler() {
// Reached the end of the playlist
}
playlist.on(clpp.events.SC_PLAYLIST_ENDED, handler);
// playlist.off(clpp.events.SC_PLAYLIST_ENDED, handler);
Content modes¶
The playlist supports two modes of operation based on the type of content it
receives. The default is VOD mode, where each item is expected to be a
static video stream.
The second mode is DVR.
DVR content mode¶
In DVR mode, every item except the last is expected to be VOD, and the
last item may be a DVR stream (a growing live stream).
While the DVR stream is live, you can’t add more items to the playlist. The DVR stream may turn static over time — at that point, you can append another DVR stream item to the end of the playlist.
Enable DVR content mode with the playlist.content option. The
playlist.endTime option must also be specified — it determines the last
item and the end time of the playlist:
const playlist = new clpp.playlist.ScPlaylist("video", {
license: "<your_license_key>",
viewerId: "<user_id>",
playlist: {
content: "DVR",
endTime: 950, // end after 950 seconds
},
});
await playlist.openPlaylist(0, [
{
id: "ASSET1",
source: { url: "..." }, // VOD
},
{
id: "ASSET2",
source: { url: "..." }, // DVR
},
]);
DVR mode adds two events on top of the basic set.
SC_PLAYLIST_LIVE_TURNED_STATIC¶
Triggered when a DVR item becomes static. After this, you can append another DVR item to the playlist:
async function handler() {
await playlist.addItem({
id: "ASSET3",
source: { url: "..." }, // DVR
});
}
playlist.on(clpp.events.SC_PLAYLIST_LIVE_TURNED_STATIC, handler);
// playlist.off(clpp.events.SC_PLAYLIST_LIVE_TURNED_STATIC, handler);
LIVE_TURNED_STATIC¶
Triggered shortly before playback reaches the end of the playlist as defined
by playlist.endTime. At this point, the playlist stops loading manifest
updates and the timeline turns static:
function handler() {
// The stream became static
playlist.isLive(); // false
}
playlist.on(clpp.events.LIVE_TURNED_STATIC, handler);
// playlist.off(clpp.events.LIVE_TURNED_STATIC, handler);