Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/bigscreenplayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,15 @@ function BigscreenPlayer() {
getInitialPlaybackTime,
getTimeShiftBufferDepthInMilliseconds,
getPresentationTimeOffsetInMilliseconds,

/**
* Updates the settings of an active player.
*
* @param {Partial<import("./types").Settings>} settings The settings to update the player with.
*/
updateSettings(settings) {
playerComponent && playerComponent.updateSettings(settings)
},
}
}

Expand Down
11 changes: 11 additions & 0 deletions src/bigscreenplayer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ describe("Bigscreen Player", () => {
setAudioDescribed: jest.fn(),
setBitrateConstraint: jest.fn(),
getPlaybackBitrate: jest.fn(),
updateSettings: jest.fn(),
}

jest.spyOn(PlayerComponent, "getLiveSupport").mockReturnValue(LiveSupport.SEEKABLE)
Expand Down Expand Up @@ -1702,4 +1703,14 @@ describe("Bigscreen Player", () => {
expect(mockPlayerComponentInstance.getPlaybackBitrate()).toBe(100)
})
})

it("should call through to PlayerComponent.updateSettings when updateSettings is called", async () => {
const settings = { updatedSetting1: true, updatedSetting2: 1 }

await asyncInitialiseBigscreenPlayer(createPlaybackElement(), bigscreenPlayerData)

bigscreenPlayer.updateSettings(settings)

expect(mockPlayerComponentInstance.updateSettings).toHaveBeenCalledWith(settings)
})
})
9 changes: 9 additions & 0 deletions src/mediasources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,14 @@ function MediaSources() {
subtitlesSources = []
}

function updateSettings(settings: {
failoverResetTime: typeof failoverResetTimeMs
failoverSort: typeof failoverSort
}) {
if (settings.failoverResetTime) failoverResetTimeMs = settings.failoverResetTime
if (settings.failoverSort) failoverSort = settings.failoverSort
}

return {
init,
failover,
Expand All @@ -381,6 +389,7 @@ function MediaSources() {
time: generateTime,
transferFormat: getCurrentTransferFormat,
tearDown,
updateSettings,
}
}

Expand Down
32 changes: 31 additions & 1 deletion src/playbackstrategy/msestrategy.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,16 @@ function MSEStrategy(
let errorCallback
let timeUpdateCallback

const seekDurationPadding = isNaN(playerSettings.streaming?.seekDurationPadding)
let seekDurationPadding = isNaN(playerSettings.streaming?.seekDurationPadding)
? DEFAULT_SETTINGS.seekDurationPadding
: playerSettings.streaming?.seekDurationPadding

const liveDelay = isNaN(playerSettings.streaming?.delay?.liveDelay)
? DEFAULT_SETTINGS.liveDelay
: playerSettings.streaming?.delay?.liveDelay

let isEnded = false

const cached = {
seekableRange: undefined,
duration: 0,
Expand Down Expand Up @@ -1017,6 +1020,32 @@ function MSEStrategy(
})
}

function updateSettings(playerSettings) {
const settings = Utils.deepClone(playerSettings)

if (settings?.failoverResetTime) {
mediaSources.updateSettings({ failoverResetTime: settings.failoverResetTime })
mediaPlayer.updateSettings({ streaming: { blacklistExpiryTime: mediaSources.failoverResetTime() } })

delete settings.failoverResetTime
}

if (settings?.failoverSort) {
mediaSources.updateSettings({ failoverSort: settings.failoverSort })

delete settings.failoverSort
}

if (settings?.streaming?.seekDurationPadding) {
seekDurationPadding = settings.streaming.seekDurationPadding

delete settings.streaming?.seekDurationPadding
}

// If we still have settings, pass them to Dash
if (Object.keys(settings).length > 0) mediaPlayer.updateSettings(settings)
}

return {
transitions: {
canBePaused: () => true,
Expand Down Expand Up @@ -1063,6 +1092,7 @@ function MSEStrategy(
getPlaybackRate: () => mediaPlayer.getPlaybackRate(),
setBitrateConstraint,
getPlaybackBitrate: (mediaKind) => currentPlaybackBitrateInKbps(mediaKind),
updateSettings,
}
}

Expand Down
71 changes: 71 additions & 0 deletions src/playbackstrategy/msestrategy.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ const mockMediaSources = {
currentProtectionData: jest.fn(),
availableSources: jest.fn().mockReturnValue([]),
failover: jest.fn().mockResolvedValue(),
updateSettings: jest.fn(),
}

describe("Media Source Extensions Playback Strategy", () => {
Expand Down Expand Up @@ -1940,4 +1941,74 @@ describe("Media Source Extensions Playback Strategy", () => {
expect(bitrate).toBe(100)
})
})

describe("Update Settings", () => {
let mseStrategy

beforeEach(() => {
mseStrategy = MSEStrategy(mockMediaSources, MediaKinds.VIDEO, playbackElement)
mseStrategy.load(null, 0)
})

it("updates Media Sources and Dash with a new failoverResetTime", () => {
const newFailoverResetTime = 42
const settings = { failoverResetTime: newFailoverResetTime }

mockMediaSources.failoverResetTime.mockReturnValueOnce(newFailoverResetTime)

mseStrategy.updateSettings(settings)

expect(mockMediaSources.updateSettings).toHaveBeenCalledWith(settings)
expect(mockDashInstance.updateSettings).toHaveBeenCalledWith({
streaming: {
blacklistExpiryTime: newFailoverResetTime,
},
})
})

it("updates Media Sources with a new failoverSort", () => {
const newFailoverSort = jest.fn()
const settings = { failoverSort: newFailoverSort }

mseStrategy.updateSettings(settings)

expect(mockMediaSources.updateSettings).toHaveBeenCalledWith(settings)
})

it("updates with a new seekDurationPadding", () => {
mockDashInstance.duration.mockReturnValueOnce(360)

const seekDurationPadding = 0.1

const mseStrategy = MSEStrategy(mockMediaSources, MediaKinds.VIDEO, playbackElement, false, {
streaming: { seekDurationPadding },
})

mseStrategy.load(null, 0)

mockDashInstance.isReady.mockReturnValue(true)
mseStrategy.setCurrentTime(360)

expect(mockDashInstance.seek).toHaveBeenCalledWith(359.9)

mediaElement.dispatchEvent(new Event("seeking"))
mediaElement.dispatchEvent(new Event("seeked"))

const newSeekDurationPadding = 1
const settings = { seekDurationPadding: newSeekDurationPadding }

mseStrategy.updateSettings(settings)
mseStrategy.setCurrentTime(360)

expect(mockDashInstance.seek).toHaveBeenCalledWith(359)
})

it("passes non BSP Extended settings to Dash", () => {
const settings = { someNonBSPExtendedSetting: true }

mseStrategy.updateSettings(settings)

expect(mockDashInstance.updateSettings).toHaveBeenCalledWith(settings)
})
})
})
5 changes: 5 additions & 0 deletions src/playercomponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,10 @@ function PlayerComponent(
return playbackStrategy?.getPlaybackBitrate(mediaKind)
}

function updateSettings(settings) {
if (playbackStrategy?.updateSettings) playbackStrategy.updateSettings(settings)
}

return {
play,
pause,
Expand All @@ -477,6 +481,7 @@ function PlayerComponent(
setSubtitles,
setBitrateConstraint,
getPlaybackBitrate,
updateSettings,
}
}

Expand Down
45 changes: 45 additions & 0 deletions src/playercomponent.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,51 @@ describe("Player Component", () => {

expect(mockAudioDescribedCallback).not.toHaveBeenCalled()
})

it("Calls through to the strategy's updateSettings if it exists", async () => {
const updateSettings = jest.fn()
const settings = { updatedSetting1: true, updatedSetting2: 1 }
const mockStrategy = createMockPlaybackStrategy(LiveSupport.SEEKABLE, { updateSettings })
const mockPlaybackStrategyClass = jest.fn().mockReturnValue(mockStrategy)
StrategyPicker.mockResolvedValueOnce(mockPlaybackStrategyClass)

const playbackElement = createPlaybackElement()

const playerComponent = new PlayerComponent(
playbackElement,
bigscreenPlayerData,
mockMediaSources,
jest.fn(),
jest.fn()
)

await jest.runOnlyPendingTimersAsync()

playerComponent.updateSettings(settings)

expect(updateSettings).toHaveBeenCalledWith(settings)
})

it("Does not throw if the strategy does not have updateSettings", async () => {
const settings = { updatedSetting1: true, updatedSetting2: 1 }
const mockStrategy = createMockPlaybackStrategy(LiveSupport.SEEKABLE)
const mockPlaybackStrategyClass = jest.fn().mockReturnValue(mockStrategy)
StrategyPicker.mockResolvedValueOnce(mockPlaybackStrategyClass)

const playbackElement = createPlaybackElement()

const playerComponent = new PlayerComponent(
playbackElement,
bigscreenPlayerData,
mockMediaSources,
jest.fn(),
jest.fn()
)

await jest.runOnlyPendingTimersAsync()

expect(() => playerComponent.updateSettings(settings)).not.toThrow()
})
})

describe("pause", () => {
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export type CaptionsConnection = Connection & {
segmentLength: number
}

type Settings = MediaPlayerSettingClass & {
export type Settings = MediaPlayerSettingClass & {
failoverResetTime: number
failoverSort: (sources: Connection[]) => Connection[]
streaming: {
Expand Down
Loading