@clappr/hlsjs-playback 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clappr/hlsjs-playback",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "HLS Playback based on hls.js",
5
5
  "main": "./dist/hlsjs-playback.js",
6
6
  "module": "./dist/hlsjs-playback.esm.js",
@@ -36,13 +36,13 @@
36
36
  },
37
37
  "homepage": "https://github.com/clappr/hlsjs-playback",
38
38
  "peerDependencies": {
39
- "@clappr/core": "^0.4.27",
40
- "hls.js": "^1.2.4"
39
+ "@clappr/core": "*",
40
+ "hls.js": "1.5.14"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@babel/core": "^7.14.2",
44
44
  "@babel/preset-env": "^7.14.2",
45
- "@clappr/core": "^0.4.27",
45
+ "@clappr/core": "^0.9.0",
46
46
  "@rollup/plugin-babel": "^5.3.0",
47
47
  "@rollup/plugin-commonjs": "^19.0.0",
48
48
  "@rollup/plugin-node-resolve": "^13.0.0",
@@ -51,7 +51,7 @@
51
51
  "coveralls": "^3.1.0",
52
52
  "cz-conventional-changelog": "^3.3.0",
53
53
  "eslint": "^7.26.0",
54
- "hls.js": "^1.2.4",
54
+ "hls.js": "1.5.14",
55
55
  "jest": "^26.6.3",
56
56
  "rollup": "^2.47.0",
57
57
  "rollup-plugin-filesize": "^9.1.1",
@@ -65,5 +65,14 @@
65
65
  "commitizen": {
66
66
  "path": "./node_modules/cz-conventional-changelog"
67
67
  }
68
+ },
69
+ "nx": {
70
+ "targets": {
71
+ "test": {
72
+ "dependsOn": [
73
+ "^release"
74
+ ]
75
+ }
76
+ }
68
77
  }
69
78
  }
package/src/hls.js CHANGED
@@ -143,6 +143,8 @@ export default class HlsjsPlayback extends HTML5Video {
143
143
  constructor(...args) {
144
144
  super(...args)
145
145
  this.options.hlsPlayback = { ...this.defaultOptions, ...this.options.hlsPlayback }
146
+ this._timeUpdateFiringRate = 0.2
147
+ this._durationChangeMinOffset = 0.5
146
148
  this._setInitialState()
147
149
  }
148
150
 
@@ -155,7 +157,7 @@ export default class HlsjsPlayback extends HTML5Video {
155
157
  this._extrapolatedWindowNumSegments = !this.options.playback || typeof (this.options.playback.extrapolatedWindowNumSegments) === 'undefined' ? 2 : this.options.playback.extrapolatedWindowNumSegments
156
158
 
157
159
  this._playbackType = Playback.VOD
158
- this._lastTimeUpdate = { current: 0, total: 0 }
160
+ this._lastTimeUpdate = { current: 0, total: 0, firstFragDateTime: 0 }
159
161
  this._lastDuration = null
160
162
  // for hls streams which have dvr with a sliding window,
161
163
  // the content at the start of the playlist is removed as new
@@ -196,7 +198,7 @@ export default class HlsjsPlayback extends HTML5Video {
196
198
 
197
199
  _destroyHLSInstance() {
198
200
  if (!this._hls) return
199
- this._manifestParsed = false
201
+ this._manifestLoading = false
200
202
  this._ccIsSetup = false
201
203
  this._ccTracksUpdated = false
202
204
  this._setInitialState()
@@ -217,7 +219,7 @@ export default class HlsjsPlayback extends HTML5Video {
217
219
  _listenHLSEvents() {
218
220
  if (!this._hls) return
219
221
  this._hls.once(HLSJS.Events.MEDIA_ATTACHED, () => { this.options.hlsPlayback.preload && this._hls.loadSource(this.options.src) })
220
- this._hls.on(HLSJS.Events.MANIFEST_PARSED, () => this._manifestParsed = true)
222
+ this._hls.on(HLSJS.Events.MANIFEST_LOADING, () => this._manifestLoading = true)
221
223
  this._hls.on(HLSJS.Events.LEVEL_LOADED, (evt, data) => this._updatePlaybackType(evt, data))
222
224
  this._hls.on(HLSJS.Events.LEVEL_UPDATED, (evt, data) => this._onLevelUpdated(evt, data))
223
225
  this._hls.on(HLSJS.Events.LEVEL_SWITCHING, (evt,data) => this._onLevelSwitch(evt, data))
@@ -265,10 +267,12 @@ export default class HlsjsPlayback extends HTML5Video {
265
267
  if (!this._recoveredDecodingError) {
266
268
  this._recoveredDecodingError = true
267
269
  this._hls.recoverMediaError()
270
+ this.play()
268
271
  } else if (!this._recoveredAudioCodecError) {
269
272
  this._recoveredAudioCodecError = true
270
273
  this._hls.swapAudioCodec()
271
274
  this._hls.recoverMediaError()
275
+ this.play()
272
276
  } else {
273
277
  Log.error('hlsjs: failed to recover', { evt, data })
274
278
  error.level = PlayerError.Levels.FATAL
@@ -333,8 +337,6 @@ export default class HlsjsPlayback extends HTML5Video {
333
337
  Log.warn('Attempt to seek to a negative time. Resetting to live point. Use seekToLivePoint() to seek to the live point.')
334
338
  time = this.getDuration()
335
339
  }
336
- // assume live if time within 3 seconds of end of stream
337
- this.dvrEnabled && this._updateDvr(time < this.getDuration()-3)
338
340
  time += this._startTime
339
341
  this.el.currentTime = time
340
342
  }
@@ -343,11 +345,6 @@ export default class HlsjsPlayback extends HTML5Video {
343
345
  this.seek(this.getDuration())
344
346
  }
345
347
 
346
- _updateDvr(status) {
347
- this.trigger(Events.PLAYBACK_DVR, status)
348
- this.trigger(Events.PLAYBACK_STATS_ADD, { 'dvr': status })
349
- }
350
-
351
348
  _updateSettings() {
352
349
  if (this._playbackType === Playback.VOD)
353
350
  this.settings.left = ['playpause', 'position', 'duration']
@@ -441,9 +438,10 @@ export default class HlsjsPlayback extends HTML5Video {
441
438
 
442
439
  _onTimeUpdate() {
443
440
  const update = { current: this.getCurrentTime(), total: this.getDuration(), firstFragDateTime: this.getProgramDateTime() }
444
- const isSame = this._lastTimeUpdate && (
445
- update.current === this._lastTimeUpdate.current &&
446
- update.total === this._lastTimeUpdate.total)
441
+ const isSameTime = Math.abs(update.current - this._lastTimeUpdate.current) < this._timeUpdateFiringRate
442
+ const isSameDuration = Math.abs(update.total - this._lastTimeUpdate.total) < this._durationChangeMinOffset
443
+ const isSameFirstFragDateTime = update.firstFragDateTime === this._lastTimeUpdate.firstFragDateTime
444
+ const isSame = isSameTime && isSameDuration && isSameFirstFragDateTime
447
445
  if (isSame) return
448
446
  this._lastTimeUpdate = update
449
447
  this.trigger(Events.PLAYBACK_TIMEUPDATE, update, this.name)
@@ -451,7 +449,8 @@ export default class HlsjsPlayback extends HTML5Video {
451
449
 
452
450
  _onDurationChange() {
453
451
  const duration = this.getDuration()
454
- if (this._lastDuration === duration) return
452
+ const isSameDuration = Math.abs(this._lastDuration - duration) < this._durationChangeMinOffset
453
+ if (isSameDuration) return
455
454
  this._lastDuration = duration
456
455
  super._onDurationChange()
457
456
  }
@@ -486,7 +485,7 @@ export default class HlsjsPlayback extends HTML5Video {
486
485
 
487
486
  play() {
488
487
  !this._hls && this._setup()
489
- !this._manifestParsed && !this.options.hlsPlayback.preload && this._hls.loadSource(this.options.src)
488
+ !this._manifestLoading && !this.options.hlsPlayback.preload && this._hls.loadSource(this.options.src)
490
489
  super.play()
491
490
  this._startTimeUpdateTimer()
492
491
  }
@@ -494,7 +493,6 @@ export default class HlsjsPlayback extends HTML5Video {
494
493
  pause() {
495
494
  if (!this._hls) return
496
495
  this.el.pause()
497
- if (this.dvrEnabled) this._updateDvr(true)
498
496
  }
499
497
 
500
498
  stop() {
package/src/hls.test.js CHANGED
@@ -151,13 +151,13 @@ describe('HlsjsPlayback', () => {
151
151
  })
152
152
 
153
153
  describe('_setup method', () => {
154
- test('sets _manifestParsed flag to false', () => {
154
+ test('sets _manifestLoading flag to false', () => {
155
155
  const playback = new HlsjsPlayback({ src: 'http://clappr.io/foo.m3u8' })
156
- expect(playback._manifestParsed).toBeUndefined()
156
+ expect(playback._manifestLoading).toBeUndefined()
157
157
 
158
158
  playback._setup()
159
159
 
160
- expect(playback._manifestParsed).toBeFalsy()
160
+ expect(playback._manifestLoading).toBeFalsy()
161
161
  })
162
162
 
163
163
  test('calls this._hls.loadSource when MEDIA_ATTACHED event is triggered and hlsPlayback.preload is true', () => {
@@ -176,15 +176,16 @@ describe('HlsjsPlayback', () => {
176
176
  expect(playback._hls.loadSource).toHaveBeenCalledTimes(1)
177
177
  })
178
178
 
179
- test('updates _manifestParsed flag value to true if MANIFEST_PARSED event is triggered', () => {
180
- const playback = new HlsjsPlayback({ src: 'http://clappr.io/foo.m3u8' })
179
+ test('updates _manifestLoading flag value to true if MANIFEST_LOADING event is triggered', () => {
180
+ const src = 'http://clappr.io/foo.m3u8'
181
+ const playback = new HlsjsPlayback({ src })
181
182
 
182
- expect(playback._manifestParsed).toBeUndefined()
183
+ expect(playback._manifestLoading).toBeUndefined()
183
184
 
184
185
  playback._setup()
185
- playback._hls.trigger(HLSJS.Events.MANIFEST_PARSED, { levels: [] })
186
+ playback._hls.trigger(HLSJS.Events.MANIFEST_LOADING, { url: src })
186
187
 
187
- expect(playback._manifestParsed).toBeTruthy()
188
+ expect(playback._manifestLoading).toBeTruthy()
188
189
  })
189
190
 
190
191
  test('calls bindCustomListeners method', () => {
@@ -241,24 +242,29 @@ describe('HlsjsPlayback', () => {
241
242
  })
242
243
 
243
244
  describe('play method', () => {
244
- test('calls this._hls.loadSource if _manifestParsed flag and options.hlsPlayback.preload are falsy', () => {
245
- const playback = new HlsjsPlayback({ src: 'http://clappr.io/foo.m3u8', hlsPlayback: { preload: true } })
245
+ test('calls this._hls.loadSource once if _manifestLoading flag and options.hlsPlayback.preload are falsy', () => {
246
+ jest.spyOn(HLSJS.prototype, 'loadSource').mockImplementation()
247
+ const src = 'http://clappr.io/foo.m3u8'
248
+ const playback = new HlsjsPlayback({ src, hlsPlayback: { preload: false } })
246
249
  playback._setup()
247
- jest.spyOn(playback._hls, 'loadSource')
248
- playback.play()
250
+ playback._hls.trigger(HLSJS.Events.MEDIA_ATTACHED, { media: playback.el })
249
251
 
250
252
  expect(playback._hls.loadSource).not.toHaveBeenCalled()
251
253
 
252
- playback.options.hlsPlayback.preload = false
253
- playback._manifestParsed = true
254
254
  playback.play()
255
+ playback._hls.trigger(HLSJS.Events.MANIFEST_LOADING, { url: src })
255
256
 
256
- expect(playback._hls.loadSource).not.toHaveBeenCalled()
257
+ expect(playback._hls.loadSource).toHaveBeenCalledTimes(1)
257
258
 
258
- playback._manifestParsed = false
259
+ // do not call loadSource again while loading a manifest
259
260
  playback.play()
260
-
261
261
  expect(playback._hls.loadSource).toHaveBeenCalledTimes(1)
262
+
263
+ playback._manifestLoading = false
264
+ playback.play()
265
+
266
+ expect(playback._hls.loadSource).toHaveBeenCalledTimes(2)
267
+ HLSJS.prototype.loadSource.mockRestore()
262
268
  })
263
269
  })
264
270