bigscreen-player
Advanced tools
Comparing version 1.2.1 to 1.3.0
@@ -7,3 +7,3 @@ { | ||
"scripts": { | ||
"build": "cp node_modules/requirejs/require.js ./ && npx webpack --config webpack.config.js", | ||
"build": "npx webpack --config webpack.config.js", | ||
"start": "http-server ../ -c -1" | ||
@@ -20,4 +20,3 @@ }, | ||
"dependencies": { | ||
"bigscreen-player": "latest", | ||
"requirejs": "2.3.6" | ||
"bigscreen-player": "latest" | ||
}, | ||
@@ -24,0 +23,0 @@ "author": "", |
# BigScreen-Player Example App | ||
This project contains a lightweight example of the `bigscreen-player` usage and was developed running in a browser and on TV playback capable devices. | ||
This project contains a lightweight example of the `bigscreen-player` usage and was developed running in a browser and on TV playback capable devices. | ||
It is hosted on [GitHub Pages](https://bbc.github.io/bigscreen-player/), and can be controlled on a browser using TAB and RETURN keys. | ||
The example is setup to perform playback via MSE (*Media Source Extensions*), using the DASH dependency. For more info regarding dependencies and development of the player itself, please see the `bigscreen-player` [README](https://github.com/bbc/bigscreen-player/blob/master/README.md), or the official [GitHub Wiki](https://github.com/bbc/bigscreen-player/wiki/Architecture-Overview). | ||
@@ -5,0 +7,0 @@ |
@@ -9,4 +9,3 @@ const path = require('path'); | ||
path: path.resolve(__dirname, 'dist/'), | ||
filename: 'index.js', | ||
libraryTarget: 'amd' | ||
filename: 'index.js' | ||
}, | ||
@@ -13,0 +12,0 @@ resolve: { |
@@ -11,3 +11,3 @@ var path = require('path'); | ||
vendor: [ | ||
'spec/lib/require-2_0_0.js' | ||
'node_modules/requirejs/require.js' | ||
], | ||
@@ -14,0 +14,0 @@ template: 'spec/support/test-runner/SpecRunner.html', |
{ | ||
"name": "bigscreen-player", | ||
"version": "1.2.1", | ||
"version": "1.3.0", | ||
"description": "Simplified media playback for bigscreen devices.", | ||
@@ -21,2 +21,3 @@ "main": "script/bigscreenplayer.js", | ||
"devDependencies": { | ||
"babel-preset-env": "1.7.0", | ||
"eslint": "^3.0.0", | ||
@@ -30,9 +31,8 @@ "eslint-plugin-es5": "1.3.1", | ||
"grunt": "^1.0.3", | ||
"grunt-contrib-jasmine": "^2.0.2", | ||
"grunt-contrib-jasmine": "^2.0.3", | ||
"jasmine": "3.2.0", | ||
"pre-commit": "^1.2.2", | ||
"pre-push": "^0.1.1", | ||
"requirejs": "2.3.6", | ||
"squirejs": "0.2.1", | ||
"babel-preset-env": "1.7.0" | ||
"requirejs": "2.0.0", | ||
"squirejs": "0.2.1" | ||
}, | ||
@@ -39,0 +39,0 @@ "dependencies": { |
@@ -19,2 +19,4 @@ # Bigscreen Player | ||
We have a working example app - see [docs/example-app](docs/example-app). | ||
### Initialisation | ||
@@ -21,0 +23,0 @@ |
@@ -395,8 +395,2 @@ require( | ||
describe('getCurrentTime', function () { | ||
it('should be set to 0 on initialisation', function () { | ||
setUpLegacyAdaptor(); | ||
expect(legacyAdaptor.getCurrentTime()).toEqual(0); | ||
}); | ||
it('should be set when we get a playing event', function () { | ||
@@ -403,0 +397,0 @@ setUpLegacyAdaptor(); |
@@ -73,3 +73,3 @@ require( | ||
mockDashInstance = jasmine.createSpyObj('mockDashInstance', | ||
['initialize', 'getDebug', 'getSource', 'on', 'off', 'time', 'duration', 'attachSource', | ||
['initialize', 'retrieveManifest', 'getDebug', 'getSource', 'on', 'off', 'time', 'duration', 'attachSource', | ||
'reset', 'isPaused', 'pause', 'play', 'seek', 'isReady', 'refreshManifest', 'getDashMetrics', 'getMetricsFor', 'setBufferToKeep', | ||
@@ -142,3 +142,3 @@ 'setBufferAheadToKeep', 'setBufferTimeAtTopQuality', 'setBufferTimeAtTopQualityLongForm', 'getBitrateInfoListFor', 'getAverageThroughput']); | ||
describe('Load', function () { | ||
describe('Load when there is no mediaPlayer', function () { | ||
it('should create a video element and add it to the media element', function () { | ||
@@ -166,28 +166,96 @@ setUpMSE(null, null, MediaKinds.VIDEO); | ||
it('should initialise MediaPlayer with the expected parameters', function () { | ||
it('should initialise MediaPlayer with the expected parameters when no start time is present', function () { | ||
setUpMSE(); | ||
mseStrategy.load('src', null, 0); | ||
mseStrategy.load('src', null, undefined); | ||
expect(mockDashInstance.initialize).toHaveBeenCalledWith(mockVideoElement, 'src', true); | ||
expect(mockDashInstance.initialize).toHaveBeenCalledWith(mockVideoElement, null, true); | ||
expect(mockDashInstance.retrieveManifest).toHaveBeenCalledWith('src', jasmine.any(Function)); | ||
}); | ||
it('should initialise MediaPlayer with the expected parameters when no start time is present', function () { | ||
setUpMSE(); | ||
mseStrategy.load('src', null, undefined); | ||
describe('for STATIC window', function () { | ||
it('should initialise MediaPlayer with the expected parameters when startTime is zero', function () { | ||
setUpMSE(); | ||
mseStrategy.load('src', null, 0); | ||
expect(mockDashInstance.initialize).toHaveBeenCalledWith(mockVideoElement, 'src', true); | ||
expect(mockDashInstance.initialize).toHaveBeenCalledWith(mockVideoElement, null, true); | ||
expect(mockDashInstance.retrieveManifest).toHaveBeenCalledWith('src', jasmine.any(Function)); | ||
}); | ||
it('should initialise MediaPlayer with the expected parameters when startTime is set', function () { | ||
setUpMSE(); | ||
mseStrategy.load('src', null, 15); | ||
expect(mockDashInstance.initialize).toHaveBeenCalledWith(mockVideoElement, null, true); | ||
expect(mockDashInstance.retrieveManifest).toHaveBeenCalledWith('src#t=15', jasmine.any(Function)); | ||
}); | ||
}); | ||
it('should initialise MediaPlayer with the expected parameters when startTime is set', function () { | ||
setUpMSE(); | ||
mseStrategy.load('src', null, 15); | ||
describe('for SLIDING window', function () { | ||
it('should initialise MediaPlayer with the expected parameters when startTime is zero', function () { | ||
setUpMSE(0, WindowTypes.SLIDING, MediaKinds.VIDEO); | ||
expect(mockDashInstance.initialize).toHaveBeenCalledWith(mockVideoElement, 'src#t=15', true); | ||
mockDashInstance.getSource.and.returnValue('src'); | ||
mseStrategy.load('src', null, 0); | ||
expect(mockDashInstance.initialize).toHaveBeenCalledWith(mockVideoElement, null, true); | ||
expect(mockDashInstance.retrieveManifest).toHaveBeenCalledWith('src', jasmine.any(Function)); | ||
}); | ||
it('should initialise MediaPlayer with the expected parameters when startTime is set to 0.1', function () { | ||
setUpMSE(0, WindowTypes.SLIDING, MediaKinds.VIDEO); | ||
mockDashInstance.getSource.and.returnValue('src'); | ||
mseStrategy.load('src', null, 0.1); | ||
expect(mockDashInstance.initialize).toHaveBeenCalledWith(mockVideoElement, null, true); | ||
expect(mockDashInstance.retrieveManifest).toHaveBeenCalledWith('src#r=0', jasmine.any(Function)); | ||
}); | ||
it('should initialise MediaPlayer with the expected parameters when startTime is set', function () { | ||
setUpMSE(0, WindowTypes.SLIDING, MediaKinds.VIDEO); | ||
mockDashInstance.getSource.and.returnValue('src'); | ||
mseStrategy.load('src', null, 100); | ||
expect(mockDashInstance.initialize).toHaveBeenCalledWith(mockVideoElement, null, true); | ||
expect(mockDashInstance.retrieveManifest).toHaveBeenCalledWith('src#r=100', jasmine.any(Function)); | ||
}); | ||
}); | ||
it('should initialise MediaPlayer with the expected parameters when startTime is set and there is a time correction', function () { | ||
setUpMSE(1922); | ||
mseStrategy.load('src', null, 15); | ||
// [ <video style="position: absolute; width: 100%; height: 100%;">, 'src#t=1937', true ] | ||
expect(mockDashInstance.initialize).toHaveBeenCalledWith(mockVideoElement, 'src#t=1937', true); | ||
describe('for GROWING window', function () { | ||
it('should initialise MediaPlayer with the expected parameters when startTime is zero', function () { | ||
setUpMSE(0, WindowTypes.GROWING, MediaKinds.VIDEO, 100000, 200000); | ||
mockDashInstance.getSource.and.returnValue('src'); | ||
mseStrategy.load('src', null, 0); | ||
expect(mockDashInstance.initialize).toHaveBeenCalledWith(mockVideoElement, null, true); | ||
expect(mockDashInstance.retrieveManifest).toHaveBeenCalledWith('src#t=101', jasmine.any(Function)); | ||
}); | ||
it('should initialise MediaPlayer with the expected parameters when startTime is set to 0.1', function () { | ||
setUpMSE(0, WindowTypes.GROWING, MediaKinds.VIDEO, 100000, 200000); | ||
mockDashInstance.getSource.and.returnValue('src'); | ||
mseStrategy.load('src', null, 0.1); | ||
expect(mockDashInstance.initialize).toHaveBeenCalledWith(mockVideoElement, null, true); | ||
expect(mockDashInstance.retrieveManifest).toHaveBeenCalledWith('src#t=101', jasmine.any(Function)); | ||
}); | ||
it('should initialise MediaPlayer with the expected parameters when startTime is set', function () { | ||
setUpMSE(0, WindowTypes.GROWING, MediaKinds.VIDEO, 100000, 200000); | ||
mockDashInstance.getSource.and.returnValue('src'); | ||
mseStrategy.load('src', null, 60); | ||
expect(mockDashInstance.initialize).toHaveBeenCalledWith(mockVideoElement, null, true); | ||
expect(mockDashInstance.retrieveManifest).toHaveBeenCalledWith('src#t=160', jasmine.any(Function)); | ||
}); | ||
}); | ||
@@ -213,4 +281,6 @@ | ||
}); | ||
}); | ||
it('should attach a new source when CDN Failover occurs', function () { | ||
describe('Load when there a mediaPlayer exists (e.g. CDN failover)', function () { | ||
it('should attach a new source with the expected parameters', function () { | ||
setUpMSE(); | ||
@@ -222,10 +292,11 @@ | ||
expect(mockDashInstance.initialize).toHaveBeenCalledWith(mockVideoElement, 'src', true); | ||
expect(mockDashInstance.initialize).toHaveBeenCalledWith(mockVideoElement, null, true); | ||
expect(mockDashInstance.retrieveManifest).toHaveBeenCalledWith('src', jasmine.any(Function)); | ||
mseStrategy.load('src2', null, 0); | ||
expect(mockDashInstance.attachSource).toHaveBeenCalledWith('src2#t=0'); | ||
expect(mockDashInstance.retrieveManifest).toHaveBeenCalledWith('src2', jasmine.any(Function)); | ||
}); | ||
it('should correctly continue playback from resume point when CDN failover occurs before we have a valid currentTime', function () { | ||
it('should a new source with the expected parameters called before we have a valid currentTime', function () { | ||
setUpMSE(); | ||
@@ -237,79 +308,30 @@ | ||
expect(mockDashInstance.initialize).toHaveBeenCalledWith(mockVideoElement, 'src#t=45', true); | ||
expect(mockDashInstance.initialize).toHaveBeenCalledWith(mockVideoElement, null, true); | ||
expect(mockDashInstance.retrieveManifest).toHaveBeenCalledWith('src#t=45', jasmine.any(Function)); | ||
mseStrategy.load('src2', null, 0); | ||
expect(mockDashInstance.attachSource).toHaveBeenCalledWith('src2#t=45'); | ||
expect(mockDashInstance.retrieveManifest).toHaveBeenCalledWith('src2#t=45', jasmine.any(Function)); | ||
mseStrategy.load('src3', null, 0); | ||
expect(mockDashInstance.attachSource).toHaveBeenCalledWith('src3#t=45'); | ||
expect(mockDashInstance.retrieveManifest).toHaveBeenCalledWith('src3#t=45', jasmine.any(Function)); | ||
}); | ||
it('should correctly continue playback from reached time when CDN failover occurs', function () { | ||
it('should attach a new source with expected parameters at the current playback time', function () { | ||
setUpMSE(); | ||
mockDashInstance.getSource.and.returnValue('src'); | ||
mockVideoElement.currentTime = 86; | ||
mseStrategy.load('src', null, 45); | ||
expect(mockDashInstance.initialize).toHaveBeenCalledWith(mockVideoElement, 'src#t=45', true); | ||
expect(mockDashInstance.initialize).toHaveBeenCalledWith(mockVideoElement, null, true); | ||
expect(mockDashInstance.retrieveManifest).toHaveBeenCalledWith('src#t=45', jasmine.any(Function)); | ||
mseStrategy.load('src2', null, 86); | ||
mockVideoElement.currentTime = 86; | ||
eventHandlers.timeupdate(); | ||
mseStrategy.load('src2', null, 0); | ||
expect(mockDashInstance.attachSource).toHaveBeenCalledWith('src2#t=86'); | ||
expect(mockDashInstance.retrieveManifest).toHaveBeenCalledWith('src2#t=86', jasmine.any(Function)); | ||
}); | ||
it('should playback from relative live start time for video simulcast', function () { | ||
setUpMSE(0, WindowTypes.SLIDING, MediaKinds.VIDEO); | ||
mockDashInstance.getSource.and.returnValue('src'); | ||
// Actual live point requested | ||
mseStrategy.load('src', null, 0); | ||
expect(mockDashInstance.initialize).toHaveBeenCalledWith(mockVideoElement, 'src#r=-1', true); | ||
}); | ||
it('should playback from relative start time for video simulcast', function () { | ||
setUpMSE(0, WindowTypes.SLIDING, MediaKinds.VIDEO); | ||
mockDashInstance.getSource.and.returnValue('src'); | ||
// Somewhat live, playbackUtils forces 0.1 for TAL related reasons! | ||
mseStrategy.load('src', null, 0.1); | ||
expect(mockDashInstance.initialize).toHaveBeenCalledWith(mockVideoElement, 'src#r=0', true); | ||
}); | ||
it('should playback from derived start time for webcast', function () { | ||
setUpMSE(0, WindowTypes.GROWING, MediaKinds.VIDEO, 100000, 200000); | ||
mockDashInstance.getSource.and.returnValue('src'); | ||
mseStrategy.load('src', null, 0.1); | ||
expect(mockDashInstance.initialize).toHaveBeenCalledWith(mockVideoElement, 'src#t=101', true); | ||
}); | ||
it('should playback from the provided time of a webcast', function () { | ||
setUpMSE(0, WindowTypes.GROWING, MediaKinds.VIDEO, 100000, 200000); | ||
mockDashInstance.getSource.and.returnValue('src'); | ||
mseStrategy.load('src', null, 60); | ||
expect(mockDashInstance.initialize).toHaveBeenCalledWith(mockVideoElement, 'src#t=160', true); | ||
}); | ||
it('should playback from the live point of a webcast', function () { | ||
setUpMSE(0, WindowTypes.GROWING, MediaKinds.VIDEO, 100000, 200000); | ||
mockDashInstance.getSource.and.returnValue('src'); | ||
mseStrategy.load('src', null, undefined); | ||
expect(mockDashInstance.initialize).toHaveBeenCalledWith(mockVideoElement, 'src', true); | ||
}); | ||
}); | ||
@@ -316,0 +338,0 @@ |
@@ -24,2 +24,3 @@ require( | ||
var mockStateUpdateCallback; | ||
var corePlaybackData; | ||
@@ -33,3 +34,3 @@ // opts = streamType, playbackType, mediaType, subtitlesAvailable, subtitlesEnabled noStatsReporter, disableUi | ||
var corePlaybackData = { | ||
corePlaybackData = { | ||
media: { | ||
@@ -40,2 +41,3 @@ kind: opts.mediaKind || MediaKinds.VIDEO, | ||
type: 'application/dash+xml', | ||
manifestType: opts.manifestType || 'mpd', | ||
bitrate: undefined, | ||
@@ -342,3 +344,3 @@ captionsUrl: opts.subtitlesAvailable ? 'captionsUrl' : undefined | ||
// playout logic | ||
// playout logic | ||
it('should fire buffering cleared on the plugins', function () { | ||
@@ -894,2 +896,5 @@ var bufferingProperties = { | ||
expect(corePlaybackData.media.urls.length).toBe(3); | ||
expect(corePlaybackData.media.urls).toContain(jasmine.objectContaining({cdn: 'cdn-a'})); | ||
jasmine.clock().tick(1); | ||
@@ -900,2 +905,5 @@ | ||
expect(mockStrategy.load).toHaveBeenCalledWith(secondCdn, type, currentTime); | ||
expect(corePlaybackData.media.urls.length).toBe(2); | ||
expect(corePlaybackData.media.urls).not.toContain(jasmine.objectContaining({cdn: 'cdn-a'})); | ||
}); | ||
@@ -959,9 +967,8 @@ | ||
it('should fire a fatal error on the plugins if the playback type is live', function () { | ||
it('should publish a media state update of fatal if playback is live hls', function () { | ||
spyOn(mockStrategy, 'getDuration').and.returnValue(100); | ||
spyOn(mockStrategy, 'getCurrentTime').and.returnValue(94); | ||
spyOn(mockStrategy, 'getSeekableRange').and.returnValue({start: 0, end: 100}); | ||
spyOn(mockStrategy, 'load'); | ||
setUpPlayerComponent({windowType: WindowTypes.SLIDING, multiCdn: true}); | ||
setUpPlayerComponent({multiCdn: true, manifestType: 'm3u8', windowType: WindowTypes.SLIDING}); | ||
@@ -974,8 +981,13 @@ mockStrategy.mockingHooks.fireErrorEvent({errorProperties: {}}); | ||
expect(mockPluginsInterface.onFatalError).toHaveBeenCalledWith(jasmine.objectContaining(pluginData)); | ||
expect(mockStateUpdateCallback.calls.mostRecent().args[0].data.state).toEqual(MediaState.FATAL_ERROR); | ||
}); | ||
it('should publish a media state update of fatal if the playback type is live', function () { | ||
it('should failover after buffering for 20 seconds on live dash playback', function () { | ||
var secondCdn = 'b'; | ||
var currentTime = 94; | ||
var type = 'application/dash+xml'; | ||
spyOn(mockStrategy, 'getDuration').and.returnValue(100); | ||
spyOn(mockStrategy, 'getCurrentTime').and.returnValue(94); | ||
spyOn(mockStrategy, 'getCurrentTime').and.returnValue(currentTime); | ||
spyOn(mockStrategy, 'getSeekableRange').and.returnValue({start: 0, end: 100}); | ||
spyOn(mockStrategy, 'load'); | ||
@@ -985,9 +997,16 @@ | ||
mockStrategy.mockingHooks.fireErrorEvent({errorProperties: {}}); | ||
// Set playback cause to normal | ||
mockStrategy.mockingHooks.fireEvent(MediaState.PLAYING); | ||
jasmine.clock().tick(5000); | ||
mockStrategy.mockingHooks.fireEvent(MediaState.WAITING); | ||
jasmine.clock().tick(19999); | ||
expect(mockStrategy.load).toHaveBeenCalledTimes(1); | ||
expect(mockStateUpdateCallback.calls.mostRecent().args[0].data.state).toEqual(MediaState.FATAL_ERROR); | ||
jasmine.clock().tick(1); | ||
expect(mockStrategy.load).toHaveBeenCalledTimes(2); | ||
expect(mockStrategy.load).toHaveBeenCalledWith(secondCdn, type, currentTime); | ||
}); | ||
@@ -994,0 +1013,0 @@ |
@@ -129,3 +129,8 @@ define('bigscreenplayer/bigscreenplayer', | ||
DebugTool.keyValue({key: 'cdn', value: bigscreenPlayerData.media.urls[0].cdn}); | ||
var availableCdns = bigscreenPlayerData.media.urls.map(function (media) { | ||
return media.cdn; | ||
}); | ||
DebugTool.keyValue({key: 'available cdns', value: availableCdns}); | ||
DebugTool.keyValue({key: 'current cdn', value: bigscreenPlayerData.media.urls[0].cdn}); | ||
DebugTool.keyValue({key: 'url', value: bigscreenPlayerData.media.urls[0].url}); | ||
@@ -185,3 +190,3 @@ }, | ||
getCurrentTime: function () { | ||
return playerComponent ? playerComponent.getCurrentTime() : 0; | ||
return playerComponent && playerComponent.getCurrentTime() || 0; | ||
}, | ||
@@ -188,0 +193,0 @@ getMediaKind: function () { |
@@ -140,3 +140,3 @@ define('bigscreenplayer/mockbigscreenplayer', | ||
endedState = false; | ||
mediaKind = 'video'; | ||
mediaKind = bigscreenPlayerData && bigscreenPlayerData.media && bigscreenPlayerData.media.kind || 'video'; | ||
windowType = newWindowType || WindowTypes.STATIC; | ||
@@ -143,0 +143,0 @@ subtitlesAvailable = true; |
@@ -19,3 +19,3 @@ define('bigscreenplayer/playbackstrategy/legacyplayeradapter', | ||
var timeUpdateCallback; | ||
var currentTime = 0; | ||
var currentTime; | ||
var timeCorrection = timeData && timeData.correction || 0; | ||
@@ -250,6 +250,7 @@ var duration = 0; | ||
hasStartTime = startTime || startTime === 0; | ||
var isLiveNonRestart = windowType !== WindowTypes.STATIC && !hasStartTime; | ||
var isPlaybackFromLivePoint = windowType !== WindowTypes.STATIC && !hasStartTime; | ||
mediaPlayer.initialiseMedia('video', src, mimeType, playbackElement, setSourceOpts); | ||
if (mediaPlayer.beginPlaybackFrom && !isLiveNonRestart) { | ||
if (mediaPlayer.beginPlaybackFrom && !isPlaybackFromLivePoint) { | ||
currentTime = startTime; | ||
mediaPlayer.beginPlaybackFrom(startTime + timeCorrection || 0); | ||
@@ -256,0 +257,0 @@ } else { |
@@ -525,3 +525,3 @@ define( | ||
if (clampedTime !== seconds) { | ||
logger.debug('playFrom ' + seconds + ' clamped to ' + clampedTime + ' - seekable range is { start: ' + range.start + ', end: ' + range.end + ' }'); | ||
logger.debug('play From ' + seconds + ' clamped to ' + clampedTime + ' - seekable range is { start: ' + range.start + ', end: ' + range.end + ' }'); | ||
} | ||
@@ -603,3 +603,8 @@ return clampedTime; | ||
initialiseMedia: function (type, url, mediaMimeType, sourceContainer, opts) { | ||
disableSentinels = opts.disableSentinels; | ||
mediaType = type; | ||
source = url; | ||
mimeType = mediaMimeType; | ||
opts = opts || {}; | ||
if (getState() === MediaPlayerBase.STATE.EMPTY) { | ||
@@ -613,7 +618,2 @@ var idSuffix = 'Video'; | ||
disableSentinels = opts.disableSentinels; | ||
mediaType = type; | ||
source = url; | ||
mimeType = mediaMimeType; | ||
mediaElement = document.createElement(idSuffix.toLowerCase(), 'mediaPlayer' + idSuffix); | ||
@@ -620,0 +620,0 @@ mediaElement.autoplay = false; |
@@ -8,2 +8,3 @@ define('bigscreenplayer/playbackstrategy/msestrategy', | ||
'bigscreenplayer/plugins', | ||
'bigscreenplayer/parsers/manifestfilter', | ||
@@ -13,3 +14,3 @@ // static imports | ||
], | ||
function (MediaState, WindowTypes, DebugTool, MediaKinds, Plugins) { | ||
function (MediaState, WindowTypes, DebugTool, MediaKinds, Plugins, ManifestFilter) { | ||
return function (windowType, mediaKind, timeData, playbackElement) { | ||
@@ -22,3 +23,3 @@ var mediaPlayer; | ||
var initialStartTime; | ||
var failoverTime; | ||
var isEnded = false; | ||
@@ -42,5 +43,3 @@ var mediaElement; | ||
METRIC_ADDED: 'metricAdded', | ||
METRIC_CHANGED: 'metricChanged', | ||
PLAYBACK_STALLED: 'playbackStalled', | ||
BUFFER_STALLED: 'bufferStalled' | ||
METRIC_CHANGED: 'metricChanged' | ||
}; | ||
@@ -73,2 +72,3 @@ | ||
function onTimeUpdate () { | ||
failoverTime = mediaElement.currentTime - timeCorrection; | ||
publishTimeUpdate(); | ||
@@ -78,4 +78,8 @@ } | ||
function onError (event) { | ||
event.errorProperties = {error_mssg: event.error}; | ||
if (event.error && event.error.data) { | ||
delete event.error.data; | ||
} | ||
event.errorProperties = { error_mssg: event.error }; | ||
if (event.error) { | ||
@@ -91,10 +95,2 @@ if (event.error.message) { | ||
function onBufferStalled (event) { | ||
DebugTool.info('MSE Buffer Stalled'); | ||
} | ||
function onPlaybackStalled (event) { | ||
DebugTool.info('Playback Element Stalled'); | ||
} | ||
function onManifestLoaded (event) { | ||
@@ -119,3 +115,3 @@ DebugTool.info('Manifest loaded. Duration is: ' + event.data.mediaPresentationDuration); | ||
var newRepresentation = event.newQuality + ' (' + playerMetadata.playbackBitrate + ' kbps)'; | ||
DebugTool.keyValue({key: event.mediaType + ' Representation', value: newRepresentation}); | ||
DebugTool.keyValue({ key: event.mediaType + ' Representation', value: newRepresentation }); | ||
DebugTool.info('ABR Change Rendered From Representation ' + oldRepresentation + ' To ' + newRepresentation); | ||
@@ -130,3 +126,3 @@ } | ||
if (event.metric === 'DroppedFrames') { | ||
DebugTool.keyValue({key: 'Dropped Frames', value: event.value.droppedFrames}); | ||
DebugTool.keyValue({ key: 'Dropped Frames', value: event.value.droppedFrames }); | ||
} | ||
@@ -140,3 +136,3 @@ } | ||
playerMetadata.bufferLength = dashMetrics.getCurrentBufferLevel(mediaMetrics); | ||
DebugTool.keyValue({key: 'Buffer Length', value: playerMetadata.bufferLength}); | ||
DebugTool.keyValue({ key: 'Buffer Length', value: playerMetadata.bufferLength }); | ||
Plugins.interface.onPlayerInfoUpdated(playerMetadata); | ||
@@ -166,3 +162,3 @@ } | ||
function isPaused () { | ||
return (mediaPlayer) ? mediaPlayer.isPaused() : undefined; | ||
return (mediaPlayer && mediaPlayer.isReady()) ? mediaPlayer.isPaused() : undefined; | ||
} | ||
@@ -197,5 +193,13 @@ | ||
mediaPlayer.initialize(mediaElement, src, true); | ||
mediaPlayer.initialize(mediaElement, null, true); | ||
modifySource(src); | ||
} | ||
function modifySource (src) { | ||
mediaPlayer.retrieveManifest(src, function (manifest) { | ||
var filteredManifest = ManifestFilter.filter(manifest, window.bigscreenPlayer.representationOptions || {}); | ||
mediaPlayer.attachSource(filteredManifest); | ||
}); | ||
} | ||
function setUpMediaListeners () { | ||
@@ -215,12 +219,34 @@ mediaElement.addEventListener('timeupdate', onTimeUpdate); | ||
mediaPlayer.on(DashJSEvents.METRIC_ADDED, onMetricAdded); | ||
mediaPlayer.on(DashJSEvents.PLAYBACK_STALLED, onPlaybackStalled); | ||
mediaPlayer.on(DashJSEvents.BUFFER_STALLED, onBufferStalled); | ||
} | ||
function cdnFailoverLoad (newSrc, currentSrcWithTime) { | ||
// When an initial playback causes a failover and the media element is still reporting 0 rather than the initial start time | ||
if (mediaElement.currentTime === 0) { | ||
currentSrcWithTime = newSrc + '#t=' + initialStartTime; | ||
/** | ||
* Calculates a source url with anchor tags for playback within dashjs | ||
* | ||
* Anchor tags applied to the MPD source for playback: | ||
* | ||
* #r - relative to the start of the first period defined in the DASH manifest | ||
* #t - time since the beginning of the first period defined in the DASH manifest | ||
* @param {String} source | ||
* @param {Number} startTime | ||
*/ | ||
function calculateSourceAnchor (source, startTime) { | ||
if (startTime === undefined || isNaN(startTime)) { | ||
return source; | ||
} | ||
mediaPlayer.attachSource(currentSrcWithTime); | ||
if (windowType === WindowTypes.STATIC) { | ||
return startTime === 0 ? source : source + '#t=' + parseInt(startTime); | ||
} | ||
if (windowType === WindowTypes.SLIDING) { | ||
return startTime === 0 ? source : source + '#r=' + parseInt(startTime); | ||
} | ||
if (windowType === WindowTypes.GROWING) { | ||
var windowStartTimeSeconds = (timeData.windowStartTime / 1000); | ||
var srcWithTimeAnchor = source + '#t='; | ||
startTime = parseInt(startTime); | ||
return startTime === 0 ? srcWithTimeAnchor + (windowStartTimeSeconds + 1) : srcWithTimeAnchor + (windowStartTimeSeconds + startTime); | ||
} | ||
} | ||
@@ -248,32 +274,10 @@ | ||
}, | ||
load: function (src, mimeType, startTime) { | ||
var srcWithTime = startTime ? src + '#t=' + (startTime + timeCorrection) : src; | ||
load: function (src, mimeType, playbackTime) { | ||
if (!mediaPlayer) { | ||
initialStartTime = startTime + timeCorrection; | ||
if (windowType === WindowTypes.GROWING) { | ||
var windowStartTimeSeconds = (timeData.windowStartTime / 1000); | ||
var srcWithTimeAnchor = src + '#t='; | ||
// No need for an absolute time to play from live with a Webcast using dashjs. | ||
srcWithTime = src; | ||
if (startTime) { | ||
startTime = parseInt(startTime); | ||
srcWithTime = startTime === 0 ? srcWithTimeAnchor + (windowStartTimeSeconds + 1) : srcWithTimeAnchor + (windowStartTimeSeconds + startTime); | ||
} | ||
} | ||
if (windowType === WindowTypes.SLIDING) { | ||
// zero start time indicates live point, relative time wise -1 will play almost from the live point, | ||
// otherwise play from the given video start time relative to the window | ||
startTime = (startTime === 0 ? -1 : startTime); | ||
srcWithTime = src + '#r=' + parseInt(startTime); | ||
} | ||
failoverTime = playbackTime; | ||
setUpMediaElement(playbackElement); | ||
setUpMediaPlayer(srcWithTime); | ||
setUpMediaPlayer(calculateSourceAnchor(src, playbackTime)); | ||
setUpMediaListeners(); | ||
} else { | ||
cdnFailoverLoad(src, srcWithTime); | ||
modifySource(calculateSourceAnchor(src, failoverTime)); | ||
} | ||
@@ -300,3 +304,3 @@ }, | ||
getDuration: function () { | ||
return (mediaPlayer) ? mediaPlayer.duration() : 0; | ||
return (mediaPlayer && mediaPlayer.isReady()) ? mediaPlayer.duration() : 0; | ||
}, | ||
@@ -319,4 +323,2 @@ tearDown: function () { | ||
mediaPlayer.off(DashJSEvents.METRIC_ADDED, onMetricAdded); | ||
mediaPlayer.off(DashJSEvents.PLAYBACK_STALLED, onPlaybackStalled); | ||
mediaPlayer.off(DashJSEvents.BUFFER_STALLED, onBufferStalled); | ||
@@ -331,3 +333,3 @@ mediaElement.parentElement.removeChild(mediaElement); | ||
isEnded = undefined; | ||
initialStartTime = undefined; | ||
failoverTime = undefined; | ||
timeCorrection = undefined; | ||
@@ -334,0 +336,0 @@ windowType = undefined; |
@@ -20,4 +20,4 @@ define('bigscreenplayer/playbackstrategy/nativestrategy', | ||
return LegacyAdapter(windowType, mediaKind, timeData, playbackElement, isUHD, device._config, mediaPlayer); | ||
return LegacyAdapter(windowType, mediaKind, timeData, playbackElement, isUHD, device.getConfig(), mediaPlayer); | ||
}; | ||
}); |
@@ -29,2 +29,3 @@ define( | ||
var fatalError; | ||
var transferFormat = bigscreenPlayerData.media.manifestType === 'mpd' ? 'dash' : 'hls'; | ||
@@ -214,4 +215,8 @@ playbackStrategy = PlaybackStrategy( | ||
function attemptCdnFailover (errorProperties, bufferingTimeoutError) { | ||
var aboutToEnd = getDuration() > 0 && (getDuration() - getCurrentTime()) <= 5; | ||
if (mediaMetaData.urls.length > 1 && windowType === WindowTypes.STATIC && !aboutToEnd) { | ||
var hasNextCDN = mediaMetaData.urls.length > 1; | ||
var aboutToEndVod = getDuration() > 0 && (getDuration() - getCurrentTime()) <= 5; | ||
var canVodFailover = windowType === WindowTypes.STATIC && !aboutToEndVod; | ||
var canLiveFailover = windowType !== WindowTypes.STATIC && transferFormat === 'dash'; | ||
if (hasNextCDN && (canVodFailover || canLiveFailover)) { | ||
cdnFailover(errorProperties, bufferingTimeoutError); | ||
@@ -231,3 +236,9 @@ } else { | ||
Plugins.interface.onErrorHandled(evt); | ||
DebugTool.keyValue({key: 'cdn', value: mediaMetaData.urls[0].cdn}); | ||
var availableCdns = mediaMetaData.urls.map(function (media) { | ||
return media.cdn; | ||
}); | ||
DebugTool.keyValue({key: 'available cdns', value: availableCdns}); | ||
DebugTool.keyValue({key: 'current cdn', value: mediaMetaData.urls[0].cdn}); | ||
DebugTool.keyValue({key: 'url', value: mediaMetaData.urls[0].url}); | ||
@@ -234,0 +245,0 @@ loadMedia(mediaMetaData.urls[0].url, mediaMetaData.type, getCurrentTime(), thenPause); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses eval() which is a dangerous function. This prevents the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
7233950
86
64834
223
1
21