Skip to content

Commit

Permalink
BADGERS-245: [BREAKING] Remove windowType metadata (#364)
Browse files Browse the repository at this point in the history
* refactor: drive dynamic utils off seekable range

* rename manifestparser and test files from .js to .ts

* feat: initial return of TimeInfo data from manifestparser, and fix upp associated tests

* feat: add time converter

* refactor: promisify media sources (untested)

* test: source loading

Co-authored-by: matt-stephenson <[email protected]>

* test: some mediaSources initialisation tests

* tests: add further assertions to media sources init tests

* test: media sources failovers

* tests: further cdn failover tests

* test: failover with servicelocation

* test: failover tests and refactoring of implementation

* test: implement subtitle sources tests

* test: implement availableSources and refresh tests

* test: implement reinstate cdn tests

* test: bigscreenplayer uses new media sources

* test: endOfStream/liveEdge tests

* test: update a bunch of bsp api tests to use new async initialisation method

* test: implement canSeek and canPause tests

* test: refactor end of stream tests. Add an additional one for pause action

* test: delete deprecated bsp methods

* style: don't export transfer formats as single values

* test: first block of msestrategy tests. Including deletion of build_source_anchor

* test: msestrategy getSeekableRange

* test: msestrategy getCurrentTime and getDuration

* test: msestrategy getPlayerElement

* test: WIP - rest tests

* test: msestrategy setCurrentTime

* test: auto-resume feature

* test: mse duration override

* test: manifestLoaded

* test: responding to manifest validity changes

* test: update tests for some more dash.js events

* test: error handling ongoing

* test: complete error handling tests

* git commit: finish mse tests

* style: better variable names

* feat: all basic strategy but autoresume

* chore: delete disableAutoResume from basicstrategy

* feat: integrate time shift detector with basic strategy

* test: native strategy does not use window type

* test: native strategy tests

* test: initial setup of legacyplayeradapter test file

* test: legacy player adapter load

* test: further legacyplayeradapter load tests

* test: legacy adapter play

* test: legacyplayeradapter play tests

* refactor: legacy player adapter pause

* refactor: legacyplayeradaptor ispaused isended

* refactor: legacyplayeradapter getDruation

* refactor: legacy player adapter getPlayerElement

* refactor: legacyplayeradapter getSeekableRange

* refactor: legacyplayeradapter getCurrentTime

* refactor: legacyplayeradapter setCurrentTime

* refactor: legacyplayeradapter playbackrate

* refactor: legacyplayeradapter reset

* refactor: legacyplayeradapter teardown

* refactor: legacyplayeradapter liveglitchcurtain

* test: legacyplaybackadapter responding to media player events

* test: legacyplaybackadapter error on exiting seek

* test: legacyplayeradapter handling delaying pause for dynamic dash streams or devides with known issues

* chore: tidy up unused variables

* chore: remove timeCorrection concept from legacyplayeradapter

* refactor: rename adaptor -> adapter in test file

* test: seekable modifier tests

* style: small tidy ups

* fix: evil jest mock sequencing

* test: restartable feature stripping

* test: implementation of restartable tests

* test: player component w/o windowtypes

* test: playercomponent setcurrenttime

* test: playercomponent onplaying

* test: finish off onPlaying tests

* test: playerComponent onPaused

* test: playercomponent onBuffering

* test: playerComponent onEnded and onTimeUpdate

* test: playercomponent onError

* test: playerComponent - start of cdn failover - horribly broken state

* test: playercomponent failover and teardown

* test: playercomponent initialise strategy

* refactor: don't pass windowtype to playback strategy

* style: tidy up test

* test: playercomponent cdnfailover tests

* style: playercomponent cleanup

* test: time conversions

* test: readyHelper (needs some love)

* refactor: remove window start time

* refactor: use failoverSubtitles promise

* test: conversion functions

* test: finish off conversion functions

* test: initial playback time

* fix: handle auto resume for time shift greater than seekable range

* refactor: remove windowtype + serverdate

* chore: fix up index.html to use new init api. Little DebugTool change

* fix: update some unit tests - removing windowType argument in PlayerComponent instantiation

* style: replace process.nextTick with jest funcs

* chore: remove todo

* style: prefer positive case

* style: move useFakeTimers to beforeAll

* chore: remove lines commented out

* chore: remove unused functions

* chore: clean up types

* refactor: use error type

* chore: remove enableSubtitles from minimal data

* test: further timeshiftdetector logic - wip

* chore: delete event history

* feat: add resilience to bad seekable range values

* chore: omit linting native playback strategies

* fix: start time shift detector on loadedmetadata

* chore: remove windowtypes usage

* refactor: take timeline in setcurrenttime

* chore: simplify index

* fix: handle mst less than pto

* style: clean up seekable

* chore: present initial playback time differently

* fix: guard safely seekable range

* feat: export new types

* style: prefix dash parse funcs with MPD

* style: better error messages

* fix: mpd catch block

* fix: export transferformat

* chore: cleanup

* spike: expose time shift buffer depth and pto

* guard accessing seekableRange properties for the debug tool

* refactor: return null from getSeekableRange until metadata loaded

* style: limit decimals on setCurrentTime log

* chore: increase the timeshiftdetector interval

* fix: force start time to .1s if it's 0 on a live stream

* handle MediaSources.time returning null, plain playback not having a mainfest to parse

* chore: rename manifestloader to sourceloader

* test: loading plain sources

* chore: add types to sourceloader

* chore: remove mimeType override

* Revert "handle MediaSources.time returning null, plain playback not having a mainfest to parse"

This reverts commit bc9b502.

* update readyHelper to accept null as a valid seekableRange for playable/restartable

* fix: jest.fakeTimers setup and usage in mseStrategy

* handle 0 start time in mse strategy the same as any other time

* feat: trace source loading

* chore: ignore unbound method errors in test files

* style: merge canPause and canSeek

* style: format existing decision records

* docs: write decision record on time representation

* docs: add decision record template

* feat: add decision record for fake seeking

* docs: decision record for timeshiftdetector

* docs: decision record for HLS AST estimate

* docs: all the tutorials

* docs: make em better

* fix: fall back to domparser

* fix: remove broken line

* docs: reword getting started intro

* docs: reword playback strategy intro

---------

Co-authored-by: matt-stephenson <[email protected]>
Co-authored-by: Joel Keers <[email protected]>
  • Loading branch information
3 people authored Feb 20, 2025
1 parent 4395e7f commit d92261d
Show file tree
Hide file tree
Showing 82 changed files with 7,667 additions and 6,859 deletions.
26 changes: 14 additions & 12 deletions docs/arch/001-native-strategy.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,23 @@

Originally Added: February 20th, 2019

# Context
## Context

* As it stands, `bigscreen-player` requires using the `tal` device object - when in `talstrategy` - to obtain a media player for playback
* Since the `tal` device media player exists as a singleton, applications using `talstrategy` can only access one active media element.
* Unlike `msestrategy`, `talstrategy` also requires loading in the extra dependency (`tal` device) - and is therefore limited by the limitations of the `tal` device.
- As it stands, `bigscreen-player` requires using the `tal` device object - when in `talstrategy` - to obtain a media player for playback
- Since the `tal` device media player exists as a singleton, applications using `talstrategy` can only access one active media element.
- Unlike `msestrategy`, `talstrategy` also requires loading in the extra dependency (`tal` device) - and is therefore limited by the limitations of the `tal` device.

# Decision
* A new strategy type - `nativestrategy` - has been created which pulls the media player out of `tal`
* This is a refactor of the `tal` device media player code which allows further manipulation and control i.e. multiple video playback
## Decision

- A new strategy type - `nativestrategy` - has been created which pulls the media player out of `tal`
- This is a refactor of the `tal` device media player code which allows further manipulation and control i.e. multiple video playback

## Status

# Status
Approved

# Consequences
* Multiple active video instances can be controlled
* Preloading of content on different media elements is more achievable
* The successful migration of all media player code from `tal` will create much greater modularity for `bigscreen-player` - therefore removing any limitations introduced by the current coupling with `tal`
## Consequences

- Multiple active video instances can be controlled
- Preloading of content on different media elements is more achievable
- The successful migration of all media player code from `tal` will create much greater modularity for `bigscreen-player` - therefore removing any limitations introduced by the current coupling with `tal`
6 changes: 3 additions & 3 deletions docs/arch/002-sinon.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ Accepted

## Consequences

* Using sinon makes it simpler to provide custom responses to network requests.
* Another third party library is now pulled into bigscreen-player as a dev dependency.
- Using sinon makes it simpler to provide custom responses to network requests.
- Another third party library is now pulled into bigscreen-player as a dev dependency.

## Further Reading

See https://sinonjs.org/ for more information
See <https://sinonjs.org/> for more information
6 changes: 3 additions & 3 deletions docs/arch/003-subtitles-polling-frequency.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Originally Added: April 7th, 2020

## Context

Subtitles in Bigscreen Player are updated by checking for the next available subtitle using a set timeout of 750ms.
Subtitles in Bigscreen Player are updated by checking for the next available subtitle using a set timeout of 750ms.
This number of 750ms was originally used with no record of why it was picked, and is also potentially too infrequent to keep the subtitles perfectly in sync with the audio and visual cues.
There was a piece of work done to increase the polling rate to 250ms, however we found that this caused some slower devices to buffer due to the increased load on the devices memory.

Expand All @@ -18,5 +18,5 @@ Accepted

## Consequences

* Synchronisation issues are negligible.
* It does not cause device performance to suffer.
- Synchronisation issues are negligible.
- It does not cause device performance to suffer.
36 changes: 36 additions & 0 deletions docs/arch/004-time-representation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# 004 Removing `windowType`; changing time representation

Originally added: 2025-02-03

## Status

| Discussing | Approved | Superceded |
| ---------- | -------- | ---------- |
| | x | |

## Context

BigscreenPlayer supports DASH and HLS streaming. Each transfer format (aka streaming protocol) represents time in a stream in a different way. BigscreenPlayer normalised these times in versions prior to v9.0.0. This normalisation made multiple assumptions:

- The timestamp in the media samples are encoded as UTC times (in seconds) for "sliding window" content i.e. streams with time shift.
- Streams with time shift never use a presentation time offset.

What is more, metadata (i.e. the `windowType`) determined BigscreenPlayer's manifest parsing strategy from v7.1.0 and codified these assumptions.

- How might we overhaul our time representation to support streams that don't comply with these assumptions?

### Considered Options

1. Expose time reported by the `MediaElement` directly. Provide functions to convert the time from the `MediaElement` into availability and media sample time.
2. Do not apply time correction based on `timeShiftBufferDepth` if `windowType === WindowTypes.GROWING`
3. Do not apply time correction based on `timeShiftBufferDepth` if SegmentTemplates in the MPD have `presentationTimeOffset`

## Decision

Chosen option: 1

This approach provides a lot of flexibility to consumers of BigscreenPlayer. It also simplifies time-related calculations such as failover, start time, and subtitle synchronisation.

## Consequences

A major version (v9.0.0) to remove window type and overhaul BigscreenPlayer's internals.
34 changes: 34 additions & 0 deletions docs/arch/005-remove-fake-seeking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# 005 Remove fake seeking from restartable strategy

Originally added: 2025-02-04

## Status

| Discussing | Approved | Superceded |
| ---------- | -------- | ---------- |
| | x | |

## Context

Native media players with the capability to start playback in a livestream from any (available) point in the stream are called "restartable" in BigscreenPlayer's jargon. Unlike "seekable" devices, "restartable" devices don't support in-stream navigation. In other words, seeking is not supported.

BigscreenPlayer exploited this restart capability to implement "fake seeking" prior to v9.0.0. The restartable player effectively polyfilled the native media player's implementation of `MediaElement#currentTime` and `MediaElement#seekable`. This polyfill relied on the `windowType` metadata to make assumptions about the shape of the stream's seekable range. v9.0.0 deprecates `windowType`.

- Should we continue to support fake seeking for native playback?
- How might we continue to support fake seeking?

### Considered Options

1. Remove fake seeking from restartable strategy
2. Poll the HLS manifest for time shift
3. Provide a "magic" time shift buffer depth for HLS streams

## Decision

Chosen option: 1

The effort to contine support for fake seeking on restartable devices is not justified by the small number of people that benefit from the continued support.

## Consequences

Viewers that use devices on the restartable strategy will no longer be able to pause or seek in-stream.
36 changes: 36 additions & 0 deletions docs/arch/006-detect-autoresume.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# 006 Detect timeshift to enable auto-resume

Originally added: 2025-02-04

## Status

| Discussing | Approved | Superceded |
| ---------- | -------- | ---------- |
| | x | |

## Context

BigscreenPlayer's auto-resume feature prevents undefined behaviour when native players resume playback outside the seekable range. Auto-resume consists of two mechanisms:

1. Playback is resumed before current time can drift outside of the seekable range
2. Pausing isn't possible when current time is close to the start of the seekable range

Auto-resume is only relevant for streams with time shift. The presence of time shift was signalled through the `windowType === WindowTypes.SLIDING` parameter prior to v9.0.0. v9.0.0 deprecates `windowType`.

DASH manifests explicitly encode the time shift of the stream through the `timeShiftBufferDepth`. On the other hand, time shift in HLS manifests is only detectable by refreshing the manifest.

- How might we detect timeshift and enable the auto-resume feature for DASH and HLS streams?

### Considered Options

1. Poll the HLS manifest to check if the first segment changes
2. Poll the seekable range for changes to the start of the seekable range
3. Provide a "magic" time shift buffer depth for HLS streams

## Decision

Chosen option: 2

## Consequences

The time it takes the `timeshiftdetector` to detect and signal timeshift depends on it's polling rate. Hence, there is a risk the user navigates outside of the seekable range in the span of time before the `timeshiftdetector` detects a sliding seekable range.
44 changes: 44 additions & 0 deletions docs/arch/007-estimate-hls-ast.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# 007 Estimate HLS Availability Start Time

Originally added: 2025-02-04

## Status

| Discussing | Approved | Superceded |
| ---------- | -------- | ---------- |
| | x | |

## Context

BigscreenPlayer adds functions to convert between three timelines:

1. Presentation time: Output by the MediaElement
2. Media sample time: Timestamps encoded in the current media
3. Availablity time: UTC times that denote time available. Only relevant for dynamic streams.

BigscreenPlayer relies on metadata in the manifest to calculate each conversion.

For DASH:

- Presentation time <-> Media sample time relies on `presentationTimeOffset`
- Presentation time <-> Availability time relies on `availabilityStartTime`

For HLS:

- Presentation time <-> Media sample time relies on `programDateTime`
- Presentation time <-> Availability time relies on ???

HLS signals availability through the segment list. An HLS media player must refresh the segment list to track availability. Availability start time can be estimated as the difference between the current wallclock time and the duration of the stream so far. This estimate should also correct for any difference between the client and server's UTC wallclock time.

### Considered Options

1. Accept the conversion between availability and presentation time is broken for HLS streams.
2. Estimate availability start time for HLS streams. This requires clients provide the offset between the client and server's UTC wallclock time in order to synchronise the calculation.

## Decision

Chosen option: 1

## Consequences

The conversion between presentation time and availability start time is erroneous for HLS.
19 changes: 19 additions & 0 deletions docs/arch/__template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# 000 Title

Originally added:

## Status

| Discussing | Approved | Superceded |
| ---------- | -------- | ---------- |
| | x | |

## Context

### [Considered Options]

Optional

## Decision

## Consequences
73 changes: 38 additions & 35 deletions docs/tutorials/00-getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ Configuration for bigscreen-player can be set using an object on the window:
window.bigscreenPlayer
```

You must provide a *playback strategy* to use BigscreenPlayer:
You must provide a _playback strategy_ to use BigscreenPlayer:

```javascript
window.bigscreenPlayer.playbackStrategy = 'msestrategy' // OR 'nativestrategy' OR 'basicstrategy'
window.bigscreenPlayer.playbackStrategy = "msestrategy" // OR 'nativestrategy' OR 'basicstrategy'
```

The MSEStrategy uses DASH. It is most likely what you want. More detail in the [documentation on playback strategies](<https://bbc.github.io/bigscreen-player/api/tutorial-01-playback-strategies.html>). You should also have a peek at the [documentation on settings and overrides](https://bbc.github.io/bigscreen-player/api/tutorial-02-settings-and-overrides.html)
The `msestrategy` uses Dash.js under the hood. It is likely to be what you want. You should read [the documentation on playback strategies](https://bbc.github.io/bigscreen-player/api/tutorial-01-playback-strategies.html) if you want to use a native media player from your browser. You should also have a peek at the [documentation on settings and overrides](https://bbc.github.io/bigscreen-player/api/tutorial-02-settings-and-overrides.html)

### Minimal Data

Expand All @@ -37,9 +37,9 @@ You must provide a manifest and its MIME type.
```javascript
const minimalData = {
media: {
type: 'application/dash+xml',
urls: [{ url: 'https://example.com/video.mpd' }]
}
type: "application/dash+xml",
urls: [{ url: "https://example.com/video.mpd" }],
},
}
```

Expand Down Expand Up @@ -67,9 +67,7 @@ playbackElement.id = 'BigscreenPlayback'

body.appendChild(playbackElement)

const enableSubtitles = false

bigscreenPlayer.init(playbackElement, optionalData, WindowTypes.STATIC, enableSubtitles)
bigscreenPlayer.init(playbackElement, minimalData)
```

## All Options
Expand All @@ -79,45 +77,50 @@ The full set of options for BigscreenPlayer is:
```javascript
const optionalData = {
initialPlaybackTime: 0, // Time (in seconds) to begin playback from
enableSubtitles: false,
media: {
type: 'application/dash+xml',
type: "application/dash+xml",
kind: MediaKinds.VIDEO, // Can be VIDEO, or AUDIO
urls: [
// Multiple urls offer the ability to fail-over to another CDN if required
{
url: 'https://example.com/video.mpd',
cdn: 'origin' // For Debug Tool reference
}, {
url: 'https://failover.example.com/video.mpd',
cdn: 'failover'
}
url: "https://example.com/video.mpd",
cdn: "origin", // For Debug Tool reference
},
{
url: "https://failover.example.com/video.mpd",
cdn: "failover",
},
],
captions: [{
url: 'https://example.com/captions/$segment$', // $segment$ required for replacement for live subtitle segments
segmentLength: 3.84, // Required to calculate live subtitle segment to fetch & live subtitle URL.
cdn: 'origin' // Displayed by Debug Tool
}, {
url: 'https://failover.example.com/captions/$segment$',
segmentLength: 3.84,
cdn: 'failover'
}
captions: [
{
url: "https://example.com/captions/$segment$", // $segment$ required for replacement for live subtitle segments
segmentLength: 3.84, // Required to calculate live subtitle segment to fetch & live subtitle URL.
cdn: "origin", // Displayed by Debug Tool
},
{
url: "https://failover.example.com/captions/$segment$",
segmentLength: 3.84,
cdn: "failover",
},
],
captionsUrl: 'https://example.com/imsc-doc.xml', // NB This parameter is being deprecated in favour of the captions array shown above.
captionsUrl: "https://example.com/imsc-doc.xml", // NB This parameter is being deprecated in favour of the captions array shown above.
subtitlesRequestTimeout: 5000, // Optional override for the XHR timeout on sidecar loaded subtitles
subtitleCustomisation: {
size: 0.75,
lineHeight: 1.10,
fontFamily: 'Arial',
backgroundColour: 'black' // (css colour, hex)
lineHeight: 1.1,
fontFamily: "Arial",
backgroundColour: "black", // (css colour, hex)
},
playerSettings: { // See settings documentation for more details
playerSettings: {
// See settings documentation for more details
failoverResetTime: 60000,
streaming: {
buffer: {
bufferToKeep: 8
}
}
}
}
bufferToKeep: 8,
},
},
},
},
}
```
Loading

0 comments on commit d92261d

Please sign in to comment.