ovenplayer 0.10.39 → 0.10.41

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": "ovenplayer",
3
- "version": "0.10.39",
3
+ "version": "0.10.41",
4
4
  "description": "OvenPlayer is Open-Source HTML5 Player. OvenPlayer supports WebRTC Signaling from OvenMediaEngine for Sub-Second Latency Streaming.",
5
5
  "main": "dist/ovenplayer.js",
6
6
  "scripts": {
@@ -1,4 +1,5 @@
1
1
  import _ from "utils/underscore";
2
+ import deepMerge from "utils/deepMerge";
2
3
 
3
4
  import {
4
5
  CONTENT_TIME_MODE_CHANGED, SYSTEM_TEXT
@@ -82,12 +83,13 @@ const Configurator = function (options, provider) {
82
83
  let currentSystemText = _.findWhere(SYSTEM_TEXT, { "lang": userCustumSystemText[i].lang });
83
84
  if (currentSystemText) {
84
85
  //validate & update
85
- Object.assign(currentSystemText, userCustumSystemText[i]);
86
+ deepMerge(currentSystemText, userCustumSystemText[i]);
86
87
  } else {
87
88
  //create
88
89
  currentSystemText = _.findWhere(SYSTEM_TEXT, { "lang": "en" });
89
90
  currentSystemText.lang = userCustumSystemText[i].lang;
90
- SYSTEM_TEXT.push(Object.assign(userCustumSystemText[i], currentSystemText));
91
+ const newMerged = deepMerge({}, currentSystemText, userCustumSystemText[i]);
92
+ SYSTEM_TEXT.push(newMerged);
91
93
  }
92
94
  }
93
95
  }
@@ -135,6 +135,9 @@ export const SYSTEM_TEXT = [
135
135
  "low_latency_p2p": "Sub-Second Latency P2P",
136
136
  },
137
137
  "playlist": "Playlist",
138
+ "quality": {
139
+ "auto": "Auto"
140
+ },
138
141
  "setting": {
139
142
  "title": "Settings",
140
143
  "speed": "Speed",
@@ -295,6 +298,9 @@ export const SYSTEM_TEXT = [
295
298
  "low_latency_p2p": "초저지연 P2P",
296
299
  },
297
300
  "playlist": "플레이리스트",
301
+ "quality": {
302
+ "auto": "자동"
303
+ },
298
304
  "setting": {
299
305
  "title": "설정",
300
306
  "speed": "재생 속도",
@@ -455,6 +461,9 @@ export const SYSTEM_TEXT = [
455
461
  "low_latency_p2p": "Transmisja z niskim opóźnieniem P2P",
456
462
  },
457
463
  "playlist": "Playlista",
464
+ "quality": {
465
+ "auto": "Auto"
466
+ },
458
467
  "setting": {
459
468
  "title": "Ustawienia",
460
469
  "speed": "Prędkość",
@@ -28,7 +28,7 @@ import {
28
28
  PROVIDER_DASH,
29
29
  PROVIDER_HLS
30
30
  } from "api/constants";
31
- import {extractVideoElement, errorTrigger} from "api/provider/utils";
31
+ import { extractVideoElement, errorTrigger } from "api/provider/utils";
32
32
 
33
33
  /**
34
34
  * @brief Trigger on various video events.
@@ -37,18 +37,18 @@ import {extractVideoElement, errorTrigger} from "api/provider/utils";
37
37
  * */
38
38
 
39
39
 
40
- const Listener = function(element, provider, videoEndedCallback, playerConfig){
40
+ const Listener = function (element, provider, videoEndedCallback, playerConfig) {
41
41
  const lowLevelEvents = {};
42
42
 
43
- OvenPlayerConsole.log("EventListener loaded.",element ,provider );
43
+ OvenPlayerConsole.log("EventListener loaded.", element, provider);
44
44
  const that = {};
45
45
 
46
46
  let stalled = -1;
47
- let elVideo = element;
47
+ let elVideo = element;
48
48
  const between = function (num, min, max) {
49
49
  return Math.max(Math.min(num, max), min);
50
50
  };
51
- const compareStalledTime = function(stalled, position){
51
+ const compareStalledTime = function (stalled, position) {
52
52
  //Original Code is stalled !== position
53
53
  //Because Dashjs is very meticulous. Then always diffrence stalled and position.
54
54
  //That is why when I use toFixed(2).
@@ -77,12 +77,12 @@ const Listener = function(element, provider, videoEndedCallback, playerConfig){
77
77
  // IE doesn't set paused property to true. So force set it.
78
78
  elVideo.pause();
79
79
 
80
- if(provider.getState() !== STATE_IDLE && provider.getState() !== STATE_COMPLETE && provider.getState() !== STATE_ERROR) {
81
- if(videoEndedCallback){
82
- videoEndedCallback(function(){
80
+ if (provider.getState() !== STATE_IDLE && provider.getState() !== STATE_COMPLETE && provider.getState() !== STATE_ERROR) {
81
+ if (videoEndedCallback) {
82
+ videoEndedCallback(function () {
83
83
  provider.setState(STATE_COMPLETE);
84
84
  });
85
- }else{
85
+ } else {
86
86
  provider.setState(STATE_COMPLETE);
87
87
  }
88
88
  }
@@ -107,8 +107,8 @@ const Listener = function(element, provider, videoEndedCallback, playerConfig){
107
107
  let sourceIndex = provider.getCurrentSource();
108
108
  let type = sourceIndex > -1 ? sources[sourceIndex].type : "";
109
109
  var metadata = {
110
- duration: provider.isLive() ? Infinity : elVideo.duration,
111
- type :type
110
+ duration: provider.isLive() ? Infinity : elVideo.duration,
111
+ type: type
112
112
  };
113
113
 
114
114
  provider.setMetaLoaded();
@@ -119,16 +119,16 @@ const Listener = function(element, provider, videoEndedCallback, playerConfig){
119
119
 
120
120
  lowLevelEvents.pause = () => {
121
121
  //Fires when the audio/video has been paused
122
- if(provider.getState() === STATE_COMPLETE || provider.getState() === STATE_ERROR){
122
+ if (provider.getState() === STATE_COMPLETE || provider.getState() === STATE_ERROR) {
123
123
  return false;
124
124
  }
125
- if(elVideo.ended){
125
+ if (elVideo.ended) {
126
126
  return false;
127
127
  }
128
- if(elVideo.error){
128
+ if (elVideo.error) {
129
129
  return false;
130
130
  }
131
- if(elVideo.currentTime === elVideo.duration){
131
+ if (elVideo.currentTime === elVideo.duration) {
132
132
  return false;
133
133
  }
134
134
  OvenPlayerConsole.log("EventListener : on pause");
@@ -157,7 +157,7 @@ const Listener = function(element, provider, videoEndedCallback, playerConfig){
157
157
  lowLevelEvents.playing = () => {
158
158
  //Fires when the audio/video is playing after having been paused or stopped for buffering
159
159
  OvenPlayerConsole.log("EventListener : on playing");
160
- if(stalled < 0){
160
+ if (stalled < 0) {
161
161
  provider.setState(STATE_PLAYING);
162
162
  }
163
163
  };
@@ -165,20 +165,20 @@ const Listener = function(element, provider, videoEndedCallback, playerConfig){
165
165
  lowLevelEvents.progress = () => {
166
166
  //Fires when the browser is downloading the audio/video
167
167
  let timeRanges = elVideo.buffered;
168
- if(!timeRanges ){
168
+ if (!timeRanges) {
169
169
  return false;
170
170
  }
171
171
 
172
172
  let duration = elVideo.duration, position = elVideo.currentTime;
173
- let buffered = between( (timeRanges.length> 0 ? timeRanges.end(timeRanges.length - 1) : 0 ) / duration, 0, 1);
173
+ let buffered = between((timeRanges.length > 0 ? timeRanges.end(timeRanges.length - 1) : 0) / duration, 0, 1);
174
174
 
175
- provider.setBuffer(buffered*100);
175
+ provider.setBuffer(buffered * 100);
176
176
  provider.trigger(CONTENT_BUFFER, {
177
- bufferPercent: buffered*100,
178
- position: position,
177
+ bufferPercent: buffered * 100,
178
+ position: position,
179
179
  duration: duration
180
180
  });
181
- OvenPlayerConsole.log("EventListener : on progress", buffered*100);
181
+ OvenPlayerConsole.log("EventListener : on progress", buffered * 100);
182
182
  };
183
183
 
184
184
 
@@ -213,12 +213,12 @@ const Listener = function(element, provider, videoEndedCallback, playerConfig){
213
213
  }
214
214
 
215
215
  //Sometimes dash live gave to me crazy duration. (9007199254740991...) why???
216
- if(duration > 9000000000000000){ //9007199254740991
216
+ if (duration > 9000000000000000) { //9007199254740991
217
217
  duration = Infinity;
218
218
  }
219
219
 
220
- if(!provider.isSeeking() && !elVideo.paused && (provider.getState() === STATE_STALLED || provider.getState() === STATE_LOADING || provider.getState() === STATE_AD_PLAYING) &&
221
- !compareStalledTime(stalled, position) ){
220
+ if (!provider.isSeeking() && !elVideo.paused && (provider.getState() === STATE_STALLED || provider.getState() === STATE_LOADING || provider.getState() === STATE_AD_PLAYING) &&
221
+ !compareStalledTime(stalled, position)) {
222
222
  stalled = -1;
223
223
  provider.setState(STATE_PLAYING);
224
224
  }
@@ -252,12 +252,12 @@ const Listener = function(element, provider, videoEndedCallback, playerConfig){
252
252
  lowLevelEvents.seeking = () => {
253
253
  provider.setSeeking(true);
254
254
  OvenPlayerConsole.log("EventListener : on seeking", elVideo.currentTime);
255
- provider.trigger(CONTENT_SEEK,{
256
- position : elVideo.currentTime
255
+ provider.trigger(CONTENT_SEEK, {
256
+ position: elVideo.currentTime
257
257
  });
258
258
  };
259
259
  lowLevelEvents.seeked = () => {
260
- if(!provider.isSeeking()){
260
+ if (!provider.isSeeking()) {
261
261
  return;
262
262
  }
263
263
  OvenPlayerConsole.log("EventListener : on seeked");
@@ -273,11 +273,10 @@ const Listener = function(element, provider, videoEndedCallback, playerConfig){
273
273
  lowLevelEvents.waiting = () => {
274
274
  //Fires when the video stops because it needs to buffer the next frame
275
275
  OvenPlayerConsole.log("EventListener : on waiting", provider.getState());
276
- if(provider.isSeeking()){
276
+ if (provider.isSeeking()) {
277
277
  provider.setState(STATE_LOADING);
278
- }else if(provider.getState() === STATE_PLAYING){
278
+ } else if (provider.getState() === STATE_PLAYING) {
279
279
  stalled = elVideo.currentTime;
280
- provider.setState(STATE_STALLED);
281
280
  }
282
281
  };
283
282
 
@@ -297,7 +296,7 @@ const Listener = function(element, provider, videoEndedCallback, playerConfig){
297
296
  2: PLAYER_UNKNWON_NETWORK_ERROR,
298
297
  3: PLAYER_UNKNWON_DECODE_ERROR,
299
298
  4: PLAYER_FILE_ERROR
300
- }[code]||0);
299
+ }[code] || 0);
301
300
 
302
301
  OvenPlayerConsole.log("EventListener : on error", convertedErroCode);
303
302
  errorTrigger(ERRORS.codes[convertedErroCode], provider);
@@ -308,7 +307,7 @@ const Listener = function(element, provider, videoEndedCallback, playerConfig){
308
307
  elVideo.addEventListener(eventName, lowLevelEvents[eventName]);
309
308
  });
310
309
 
311
- that.destroy = () =>{
310
+ that.destroy = () => {
312
311
  OvenPlayerConsole.log("EventListener : destroy()");
313
312
 
314
313
  Object.keys(lowLevelEvents).forEach(eventName => {
@@ -20,6 +20,7 @@ const WebRTC = function (element, playerConfig, adTagUrl) {
20
20
  let webrtcLoader = null;
21
21
  let superDestroy_func = null;
22
22
  let superPlay_func = null;
23
+ let superStop_func = null;
23
24
 
24
25
  let sourceFile = null;
25
26
 
@@ -255,6 +256,7 @@ const WebRTC = function (element, playerConfig, adTagUrl) {
255
256
 
256
257
  superDestroy_func = that.super('destroy');
257
258
  superPlay_func = that.super('play');
259
+ superStop_func = that.super('stop');
258
260
 
259
261
  OvenPlayerConsole.log("WEBRTC PROVIDER LOADED.");
260
262
 
@@ -277,14 +279,19 @@ const WebRTC = function (element, playerConfig, adTagUrl) {
277
279
 
278
280
  that.play = () => {
279
281
 
280
- if (timeoutMaxRetry > 0 && !connected) {
281
-
282
+ if (!webrtcLoader || (timeoutMaxRetry > 0 && !connected)) {
282
283
  loadWebRTCLoader();
283
284
  }
284
285
 
285
286
  superPlay_func();
286
287
  };
287
288
 
289
+ that.stop = () => {
290
+ clearTimeout(connectionCheckTimer);
291
+ destroyWebRtcLoader();
292
+ superStop_func();
293
+ };
294
+
288
295
  return that;
289
296
  };
290
297
 
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Performs a deep merge of the `source` object's properties into the `target` object.
3
+ * For nested objects, the function recurses; otherwise it directly overwrites values.
4
+ *
5
+ * @param {Object} target - The object to which properties are merged.
6
+ * @param {Object} source - The object containing properties to be copied.
7
+ * @returns {Object} The modified `target` object reference.
8
+ *
9
+ * @example
10
+ * const objA = {
11
+ * a: 1,
12
+ * b: { x: 10, y: 20 }
13
+ * };
14
+ * const objB = {
15
+ * b: { y: 999, z: 50 },
16
+ * c: 3
17
+ * };
18
+ *
19
+ * deepMerge(objA, objB);
20
+ * Result: objA = {
21
+ * a: 1,
22
+ * b: { x: 10, y: 999, z: 50 },
23
+ * c: 3
24
+ * }
25
+ */
26
+ export default function deepMerge(target, source) {
27
+ for (let key in source) {
28
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
29
+ if (
30
+ typeof source[key] === "object" &&
31
+ source[key] !== null &&
32
+ typeof target[key] === "object" &&
33
+ target[key] !== null
34
+ ) {
35
+ deepMerge(target[key], source[key]);
36
+ } else {
37
+ target[key] = source[key];
38
+ }
39
+ }
40
+ }
41
+ return target;
42
+ }
@@ -231,28 +231,6 @@ const ProgressBar = function ($container, api, isAd, metadata) {
231
231
 
232
232
  let time = (durationForCalc || 0) * percentage;
233
233
 
234
- if (hlsLive && !nativeHlsLive) {
235
-
236
- // if latency control is on. temporarily disable latency control
237
- const config = api.getConfig();
238
- if (config.hlsConfig) {
239
-
240
- if (typeof config.hlsConfig.liveSyncDuration === 'number') {
241
- api.getMseInstance().config.liveSyncDuration = undefined;
242
- }
243
-
244
- if (typeof config.hlsConfig.liveMaxLatencyDuration === 'number') {
245
- api.getMseInstance().config.liveMaxLatencyDuration = undefined;
246
- }
247
-
248
- if (typeof config.hlsConfig.maxLiveSyncPlaybackRate === 'number') {
249
- api.getMseInstance().config.maxLiveSyncPlaybackRate = 1;
250
- }
251
- }
252
-
253
- time = (durationForCalc - api.getDvrWindow()) + api.getDvrWindow() * percentage;
254
- }
255
-
256
234
  if (hlsLive && nativeHlsLive) {
257
235
  const dvrWindow = getNativeHlsDvrWindow();
258
236
  time = (durationForCalc - dvrWindow) + dvrWindow * percentage;
@@ -106,16 +106,17 @@ const Panels = function ($container, api, data) {
106
106
 
107
107
  } else if (panelType === "quality") {
108
108
  let qualityLevels = api.getQualityLevels();
109
+ let isQualityCheck = api.isAutoQuality();
109
110
  panel.body.push({
110
- title: "AUTO",
111
- isCheck: api.isAutoQuality(),
111
+ title: playerConfig.systemText.ui.quality.auto,
112
+ isCheck: isQualityCheck,
112
113
  value: "AUTO",
113
114
  panelType: panelType
114
115
  });
115
116
  for (let i = 0; i < qualityLevels.length; i++) {
116
117
  let body = {
117
118
  title: qualityLevels[i].label,
118
- isCheck: api.getCurrentQuality() === i,
119
+ isCheck: !isQualityCheck && api.getCurrentQuality() === i,
119
120
  value: i,
120
121
  panelType: panelType
121
122
  };
@@ -31,7 +31,7 @@ const QualityPanel = function($container, api, data){
31
31
  if( $panel.find(".op-setting-item-checked").hasClass("op-show")){
32
32
  $panel.find(".op-setting-item-checked").removeClass("op-show");
33
33
  }
34
- if(newQuality === parseInt($panel.attr("op-data-value"))){
34
+ if(!data.isAuto && newQuality === parseInt($panel.attr("op-data-value"))){
35
35
  $panel.find(".op-setting-item-checked").addClass("op-show");
36
36
  }
37
37
  if(data.isAuto && $panel.attr("op-data-value") === "AUTO"){
@@ -19,6 +19,8 @@ const TimeDisplay = function ($container, api, data) {
19
19
  let hlsLive = false;
20
20
  let nativeHlsLive = false;
21
21
 
22
+ let currVodPosition = 0;
23
+
22
24
  function convertHumanizeTime(time) {
23
25
  return naturalHms(time);
24
26
  }
@@ -29,6 +31,7 @@ const TimeDisplay = function ($container, api, data) {
29
31
  }
30
32
 
31
33
  const onRendered = function ($current, template) {
34
+ currVodPosition = 0;
32
35
  let isTimecode = api.isTimecodeMode();
33
36
  $position = $current.find(".op-time-current");
34
37
  $duration = $current.find(".op-time-duration");
@@ -48,19 +51,25 @@ const TimeDisplay = function ($container, api, data) {
48
51
  if (isTimecode) {
49
52
  $duration.text(convertHumanizeTime(data.duration));
50
53
  } else {
54
+ $position.text(0);
51
55
  $duration.text(Math.round(data.duration * api.getFramerate()) + " (" + api.getFramerate() + "fps)");
52
56
  }
53
57
 
54
58
  api.on(CONTENT_TIME_MODE_CHANGED, function (isTimecodeMode) {
55
59
  isTimecode = isTimecodeMode;
56
60
  if (isTimecode) {
61
+ $position.text(convertHumanizeTime(currVodPosition));
57
62
  $duration.text(convertHumanizeTime(data.duration));
58
63
  } else {
64
+ $position.text(Math.round(currVodPosition * api.getFramerate()));
59
65
  $duration.text(Math.round(data.duration * api.getFramerate()) + " (" + api.getFramerate() + "fps)");
60
66
  }
61
67
  }, template);
62
68
 
63
69
  api.on(CONTENT_TIME, function (data) {
70
+
71
+ currVodPosition = data.position;
72
+
64
73
  if (isTimecode) {
65
74
  $position.text(convertHumanizeTime(data.position));
66
75
  } else {
@@ -71,7 +80,7 @@ const TimeDisplay = function ($container, api, data) {
71
80
  if (hlsLive && !nativeHlsLive) {
72
81
  api.on(CONTENT_TIME, function (data) {
73
82
  if (!api.getConfig().legacyUI) {
74
- if (data.duration - data.position > 3) {
83
+ if (api.getMseInstance().liveSyncPosition - data.position > api.getMseInstance().targetLatency) {
75
84
  $liveBadge.addClass('op-live-badge-delayed');
76
85
  } else {
77
86
  $liveBadge.removeClass('op-live-badge-delayed');
@@ -105,29 +114,11 @@ const TimeDisplay = function ($container, api, data) {
105
114
 
106
115
  event.preventDefault();
107
116
 
108
- api.seek(Number.MAX_SAFE_INTEGER);
109
-
110
- //When playback get back to the latest, turn the latency control back on.
111
- const config = api.getConfig();
112
- if (config.hlsConfig) {
113
-
114
- const hlsConfig = config.hlsConfig;
115
- if (typeof hlsConfig.liveSyncDuration === 'number') {
116
- api.getMseInstance().config.liveSyncDuration
117
- = hlsConfig.liveSyncDuration;
118
- }
119
-
120
- if (typeof hlsConfig.liveMaxLatencyDuration === 'number') {
121
- api.getMseInstance().config.liveMaxLatencyDuration
122
- = hlsConfig.liveMaxLatencyDuration;
123
- }
124
-
125
- if (typeof hlsConfig.maxLiveSyncPlaybackRate === 'number') {
126
- api.getMseInstance().config.maxLiveSyncPlaybackRate =
127
- hlsConfig.maxLiveSyncPlaybackRate;
128
- }
117
+ if (hlsLive && !nativeHlsLive) {
118
+ const syncPosition = api.getMseInstance().liveSyncPosition;
119
+ api.seek(syncPosition);
129
120
  }
130
- },
121
+ }
131
122
  };
132
123
 
133
124
  return OvenTemplate($container, "TimeDisplay", api.getConfig(), data, events, onRendered, onDestroyed);