@reactoo/watchtogether-sdk-js 2.5.55 → 2.5.56
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
|
@@ -16,6 +16,7 @@ import syncDoris from "../modules/sync-modules/sync-doris";
|
|
|
16
16
|
import syncDisabled from "../modules/sync-modules/sync-disabled";
|
|
17
17
|
import syncDaznDash from "../modules/sync-modules/sync-dazn-dash";
|
|
18
18
|
import syncUniversal from "../modules/sync-modules/sync-universal";
|
|
19
|
+
import syncModule from "../modules/sync-modules/sync-module";
|
|
19
20
|
|
|
20
21
|
let roomSession = function ({roomId, pinHash, role}, room, wt) {
|
|
21
22
|
|
|
@@ -194,6 +195,7 @@ let roomSession = function ({roomId, pinHash, role}, room, wt) {
|
|
|
194
195
|
}
|
|
195
196
|
},
|
|
196
197
|
|
|
198
|
+
// DAZN interface
|
|
197
199
|
renderPlayer: function (playerWrapper, fullscreenElement, roomId) {
|
|
198
200
|
try {
|
|
199
201
|
this.syncModule = syncUniversal({room, wt, roomSession: this, emitter});
|
|
@@ -209,25 +211,38 @@ let roomSession = function ({roomId, pinHash, role}, room, wt) {
|
|
|
209
211
|
|
|
210
212
|
this.detachPlayer();
|
|
211
213
|
|
|
212
|
-
if
|
|
214
|
+
if(type === 'universal') {
|
|
215
|
+
this.syncModule = syncModule({room, emitter});
|
|
216
|
+
if (this.syncModule.__events) {
|
|
217
|
+
addEvents(this.syncModule.__events);
|
|
218
|
+
}
|
|
219
|
+
this.syncModule.initialize(inputs);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Everything below is for legacy player and will be removed in the future
|
|
223
|
+
|
|
224
|
+
else if (type === 'hlsjs') {
|
|
213
225
|
this.syncModule = syncHlsJs({room, wt, roomSession: this, emitter});
|
|
214
226
|
if (this.syncModule.__events) {
|
|
215
227
|
addEvents(this.syncModule.__events);
|
|
216
228
|
}
|
|
217
229
|
this.syncModule.initialize(inputs);
|
|
218
|
-
}
|
|
230
|
+
}
|
|
231
|
+
else if (type === 'hlsjs-vod') {
|
|
219
232
|
this.syncModule = syncVodHlsJs({room, wt, roomSession: this, emitter});
|
|
220
233
|
if (this.syncModule.__events) {
|
|
221
234
|
addEvents(this.syncModule.__events);
|
|
222
235
|
}
|
|
223
236
|
this.syncModule.initialize(inputs);
|
|
224
|
-
}
|
|
237
|
+
}
|
|
238
|
+
else if (type === 'hlsnative-vod') {
|
|
225
239
|
this.syncModule = syncVodNativeHls({room, wt, roomSession: this, emitter});
|
|
226
240
|
if (this.syncModule.__events) {
|
|
227
241
|
addEvents(this.syncModule.__events);
|
|
228
242
|
}
|
|
229
243
|
this.syncModule.initialize(inputs);
|
|
230
|
-
}
|
|
244
|
+
}
|
|
245
|
+
else if (type === 'hlsnative') {
|
|
231
246
|
this.syncModule = syncNativeHls({room, wt, roomSession: this, emitter});
|
|
232
247
|
if (this.syncModule.__events) {
|
|
233
248
|
addEvents(this.syncModule.__events);
|
|
@@ -263,13 +278,15 @@ let roomSession = function ({roomId, pinHash, role}, room, wt) {
|
|
|
263
278
|
addEvents(this.syncModule.__events);
|
|
264
279
|
}
|
|
265
280
|
this.syncModule.initialize(inputs);
|
|
266
|
-
}
|
|
281
|
+
}
|
|
282
|
+
else if (type === 'dazn-dash') {
|
|
267
283
|
this.syncModule = syncDaznDash({room, wt, roomSession: this, emitter});
|
|
268
284
|
if (this.syncModule.__events) {
|
|
269
285
|
addEvents(this.syncModule.__events);
|
|
270
286
|
}
|
|
271
287
|
this.syncModule.initialize(inputs);
|
|
272
|
-
}
|
|
288
|
+
}
|
|
289
|
+
else if (type === 'disabled') {
|
|
273
290
|
this.syncModule = syncDisabled({room, wt, roomSession: this, emitter});
|
|
274
291
|
if (this.syncModule.__events) {
|
|
275
292
|
addEvents(this.syncModule.__events);
|
|
@@ -0,0 +1,607 @@
|
|
|
1
|
+
import {setExactTimeout} from "../wt-utils";
|
|
2
|
+
import localEmitter from "../wt-emitter";
|
|
3
|
+
|
|
4
|
+
const syncModule = function ({room, emitter} = {}) {
|
|
5
|
+
|
|
6
|
+
//SYNC VARS
|
|
7
|
+
let _emitter = localEmitter();
|
|
8
|
+
|
|
9
|
+
let _playerInterface = null;
|
|
10
|
+
let _playerInterfaceOptions = {
|
|
11
|
+
type: 'none',
|
|
12
|
+
ignoreBufferedTimeRanges: true,
|
|
13
|
+
disableFastSeek: false
|
|
14
|
+
};
|
|
15
|
+
const syncDefaultWaitTime = 60000;
|
|
16
|
+
const syncShortWaitTime = 10000;
|
|
17
|
+
const maxSyncThreshold = 0.5;
|
|
18
|
+
const maxSyncTries = 3;
|
|
19
|
+
const waitForPlayingEventAfterSeek = 5000;
|
|
20
|
+
const fastForwardThreshold = 4
|
|
21
|
+
let currentSyncRetry = 0;
|
|
22
|
+
let syncWaitId = null;
|
|
23
|
+
let syncNextWaitTime = null;
|
|
24
|
+
let stopFlag = false;
|
|
25
|
+
let isSyncing = false;
|
|
26
|
+
|
|
27
|
+
let playbackRate = 2;
|
|
28
|
+
let isPlaying = false;
|
|
29
|
+
let isPreloading = true;
|
|
30
|
+
let isProgrammaticallySeeked = false;
|
|
31
|
+
let shouldPropagateMaster = false;
|
|
32
|
+
|
|
33
|
+
const startSyncLoop = () => {
|
|
34
|
+
|
|
35
|
+
if(!isConnected()) {
|
|
36
|
+
room._log('--- Sync loop will not start due to user not connected yet ---');
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if(syncWaitId) {
|
|
41
|
+
room._log('--- Sync loop already running ---');
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
room._log('--- Sync enabled ---');
|
|
46
|
+
|
|
47
|
+
stopFlag = false;
|
|
48
|
+
|
|
49
|
+
const loop = () => {
|
|
50
|
+
|
|
51
|
+
isSyncing = true;
|
|
52
|
+
emitter.emit('playerSyncing', true);
|
|
53
|
+
|
|
54
|
+
sync().finally(() => {
|
|
55
|
+
|
|
56
|
+
isSyncing = false;
|
|
57
|
+
emitter.emit('playerSyncing', false);
|
|
58
|
+
|
|
59
|
+
if(isConnected() && !stopFlag) {
|
|
60
|
+
syncWaitId = setTimeout(loop, syncNextWaitTime);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
room._log('--- Automatic stop due to user not connected or stop flag enabled ---');
|
|
64
|
+
stopSyncLoop();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
})
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
loop();
|
|
71
|
+
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const stopSyncLoop = () => {
|
|
75
|
+
room._log('--- Sync disabled ---');
|
|
76
|
+
clearTimeout(syncWaitId);
|
|
77
|
+
syncWaitId = null;
|
|
78
|
+
currentSyncRetry = 0
|
|
79
|
+
stopFlag = true;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const restartSyncLoop = () => {
|
|
83
|
+
|
|
84
|
+
room._log('--- Sync restarting ---');
|
|
85
|
+
stopSyncLoop();
|
|
86
|
+
startSyncLoop();
|
|
87
|
+
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const setNextWaitTime = (didSyncFail = false) => {
|
|
91
|
+
|
|
92
|
+
if(!didSyncFail) {
|
|
93
|
+
syncNextWaitTime = syncDefaultWaitTime;
|
|
94
|
+
currentSyncRetry = 0
|
|
95
|
+
} else {
|
|
96
|
+
currentSyncRetry++;
|
|
97
|
+
if(currentSyncRetry > maxSyncTries) {
|
|
98
|
+
syncNextWaitTime = syncDefaultWaitTime
|
|
99
|
+
currentSyncRetry = 0;
|
|
100
|
+
} else {
|
|
101
|
+
syncNextWaitTime = syncShortWaitTime;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
room._log('--- Next sync will occur in ' + syncNextWaitTime / 1000 + ' seconds ---');
|
|
106
|
+
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const sync = () => {
|
|
110
|
+
|
|
111
|
+
return getSyncData()
|
|
112
|
+
.then(syncData => {
|
|
113
|
+
|
|
114
|
+
if(syncData.isMaster) {
|
|
115
|
+
if(_playerInterface.isPaused) _playerInterface.play();
|
|
116
|
+
setNextWaitTime(false);
|
|
117
|
+
return Promise.resolve();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
else if(shouldPropagateMaster) {
|
|
121
|
+
setNextWaitTime(false);
|
|
122
|
+
return propagateMasterFunc();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
else {
|
|
126
|
+
|
|
127
|
+
const syncStartTime = Date.now();
|
|
128
|
+
const {position, realPosition, isBufferSufficient} = _playerInterface.getTimeDifference
|
|
129
|
+
? _playerInterface.getTimeDifference(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping)
|
|
130
|
+
: getTimeDifference(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping);
|
|
131
|
+
if(position && Math.abs(position) <= maxSyncThreshold) {
|
|
132
|
+
room._log(`We're within max sync threshold, no need to resync now`);
|
|
133
|
+
setNextWaitTime(false);
|
|
134
|
+
return Promise.resolve();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if(position !== null) {
|
|
138
|
+
return seekBy(position)
|
|
139
|
+
.then(() => {
|
|
140
|
+
const seekDuration = (Date.now() - syncStartTime) / 1000;
|
|
141
|
+
const {position, realPosition, isBufferSufficient} = _playerInterface.getTimeDifference
|
|
142
|
+
? _playerInterface.getTimeDifference(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping)
|
|
143
|
+
: getTimeDifference(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping);
|
|
144
|
+
|
|
145
|
+
const syncPrecision = Math.abs(realPosition || position);
|
|
146
|
+
room._log(`Insufficient buffer: `, !isBufferSufficient)
|
|
147
|
+
room._log(`Seek duration is ${seekDuration} seconds`);
|
|
148
|
+
room._log(`Sync precision should be ${syncPrecision}`);
|
|
149
|
+
|
|
150
|
+
const didSyncFail = syncPrecision > maxSyncThreshold;
|
|
151
|
+
setNextWaitTime(didSyncFail);
|
|
152
|
+
return Promise.resolve();
|
|
153
|
+
})
|
|
154
|
+
.catch((e) => {
|
|
155
|
+
setNextWaitTime(true);
|
|
156
|
+
return Promise.reject(e);
|
|
157
|
+
});
|
|
158
|
+
} else {
|
|
159
|
+
setNextWaitTime(true);
|
|
160
|
+
return Promise.reject();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}).catch(() => {
|
|
164
|
+
setNextWaitTime(true);
|
|
165
|
+
return Promise.reject();
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const handleAddLocalParticipant = () => {
|
|
170
|
+
if(shouldPropagateMaster) {
|
|
171
|
+
propagateMasterFunc();
|
|
172
|
+
}
|
|
173
|
+
if(_playerInterface.isPaused === false) {
|
|
174
|
+
_emitter.once('timeupdate', restartSyncLoop);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const handleAddRemoteParticipant = () => {
|
|
179
|
+
if(shouldPropagateMaster) {
|
|
180
|
+
propagateMasterFunc();
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const isConnected = () => {
|
|
185
|
+
return room._isDataChannelOpen && room.isConnected;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const parseDataEvents = (msg = {}) => {
|
|
189
|
+
if(msg.videoroom === 'sync_request') {
|
|
190
|
+
roomSyncSend(msg.sync_slave_id).catch(()=>{});
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const getCurrentPlayerPosition = () => {
|
|
195
|
+
let position = _playerInterface.currentTime;
|
|
196
|
+
return isNaN(position) ? 0 : position * 1000
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const getTimeDifference = (fragmentSn, fragmentPos, ping) => {
|
|
200
|
+
|
|
201
|
+
let seekRanges = _playerInterface.buffered;
|
|
202
|
+
let position = (fragmentPos + (ping / 2)) / 1000;
|
|
203
|
+
let currentPlayerPosition = getCurrentPlayerPosition() / 1000;
|
|
204
|
+
|
|
205
|
+
if(seekRanges && !_playerInterfaceOptions.ignoreBufferedTimeRanges) {
|
|
206
|
+
let seekRange = {};
|
|
207
|
+
for (let i = 0; i < seekRanges.length; i++) {
|
|
208
|
+
let _c_start = seekRanges.start(i);
|
|
209
|
+
let _c_end = seekRanges.end(i);
|
|
210
|
+
if(!seekRange.start || _c_start < seekRange.start) {
|
|
211
|
+
seekRange.start = _c_start;
|
|
212
|
+
}
|
|
213
|
+
if(!seekRange.end || _c_end > seekRange.end) {
|
|
214
|
+
seekRange.end = _c_end;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if(position > seekRange.start && position < seekRange.end) {
|
|
218
|
+
return { position: position - currentPlayerPosition, realPosition: position - currentPlayerPosition, isBufferSufficient: true };
|
|
219
|
+
} else if(position < seekRange.start) {
|
|
220
|
+
room._log(`Syncing to ${seekRange.start} instead of ${position} due to lack of buffered data`);
|
|
221
|
+
return { position: seekRange.start + 0.5 - currentPlayerPosition, realPosition: position - currentPlayerPosition, isBufferSufficient: false};
|
|
222
|
+
} else if(position > seekRange.end) {
|
|
223
|
+
room._log(`Syncing to ${seekRange.end} instead of ${position} due to lack of buffered data`);
|
|
224
|
+
return { position: seekRange.end - 0.5 - currentPlayerPosition, realPosition: position - currentPlayerPosition, isBufferSufficient: false};
|
|
225
|
+
} else
|
|
226
|
+
return {position: null, isBufferSufficient: false}
|
|
227
|
+
} else {
|
|
228
|
+
return { position: position - currentPlayerPosition, realPosition: position - currentPlayerPosition, isBufferSufficient: true };
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const seekBy = (time) => {
|
|
233
|
+
return new Promise((resolve, reject) => {
|
|
234
|
+
if(_playerInterface.currentTime !== time) {
|
|
235
|
+
if(time > 0 && time < fastForwardThreshold && !_playerInterfaceOptions.disableFastSeek) {
|
|
236
|
+
room._log(`Fast forward to seek...`);
|
|
237
|
+
let wasPaused = false;
|
|
238
|
+
let _handleFailed = () => {
|
|
239
|
+
if(wasPaused) {
|
|
240
|
+
_playerInterface.pause();
|
|
241
|
+
}
|
|
242
|
+
_playerInterface.setPlaybackRate(1);
|
|
243
|
+
isProgrammaticallySeeked = false;
|
|
244
|
+
reject('Stalled');
|
|
245
|
+
};
|
|
246
|
+
let _fastForward = () => {
|
|
247
|
+
isProgrammaticallySeeked = true;
|
|
248
|
+
setExactTimeout(() => {
|
|
249
|
+
_emitter.off('stalled', _handleFailed);
|
|
250
|
+
if(wasPaused && _playerInterface.isVod) {
|
|
251
|
+
_playerInterface.pause();
|
|
252
|
+
}
|
|
253
|
+
_playerInterface.setPlaybackRate(1);
|
|
254
|
+
isProgrammaticallySeeked = false;
|
|
255
|
+
resolve();
|
|
256
|
+
}, (time * 1000) / (playbackRate - 1), 20);
|
|
257
|
+
_playerInterface.setPlaybackRate(playbackRate);
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
_emitter.once('stalled', _handleFailed);
|
|
261
|
+
|
|
262
|
+
if(_playerInterface.isPaused) {
|
|
263
|
+
wasPaused = true;
|
|
264
|
+
_playerInterface.play()
|
|
265
|
+
.then(() => {
|
|
266
|
+
_fastForward();
|
|
267
|
+
})
|
|
268
|
+
.catch(_handleFailed);
|
|
269
|
+
} else {
|
|
270
|
+
_fastForward();
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
room._log(`Jump to seek...`);
|
|
275
|
+
let currentTimestamp = Date.now();
|
|
276
|
+
let __failsafeId = null
|
|
277
|
+
let _resolve = () => {
|
|
278
|
+
clearTimeout(__failsafeId);
|
|
279
|
+
__failsafeId = null;
|
|
280
|
+
isProgrammaticallySeeked = false;
|
|
281
|
+
room._log(`It took the player ${(Date.now() - currentTimestamp) / 1000} seconds to seek `);
|
|
282
|
+
resolve();
|
|
283
|
+
};
|
|
284
|
+
isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm
|
|
285
|
+
_emitter.once('playing', _resolve);
|
|
286
|
+
__failsafeId = setTimeout(_resolve, waitForPlayingEventAfterSeek);
|
|
287
|
+
_playerInterface.seek(_playerInterface.currentTime + time);
|
|
288
|
+
}
|
|
289
|
+
} else resolve()
|
|
290
|
+
});
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
const seekTo = (time) => {
|
|
294
|
+
return new Promise((resolve, reject) => {
|
|
295
|
+
if(_playerInterface.currentTime !== time) {
|
|
296
|
+
let diff = time - _playerInterface.currentTime;
|
|
297
|
+
if(_playerInterface.currentTime < time && diff < fastForwardThreshold && !_playerInterfaceOptions.disableFastSeek) {
|
|
298
|
+
room._log(`Fast forward to seek...`);
|
|
299
|
+
let wasPaused = false;
|
|
300
|
+
let _handleFailed = () => {
|
|
301
|
+
if(wasPaused) {
|
|
302
|
+
_playerInterface.pause();
|
|
303
|
+
}
|
|
304
|
+
_playerInterface.setPlaybackRate(1);
|
|
305
|
+
isProgrammaticallySeeked = false;
|
|
306
|
+
reject('Stalled');
|
|
307
|
+
};
|
|
308
|
+
let _fastForward = () => {
|
|
309
|
+
isProgrammaticallySeeked = true;
|
|
310
|
+
setExactTimeout(() => {
|
|
311
|
+
_emitter.off('stalled', _handleFailed);
|
|
312
|
+
if(wasPaused && _playerInterface.isVod) {
|
|
313
|
+
_playerInterface.pause();
|
|
314
|
+
}
|
|
315
|
+
_playerInterface.setPlaybackRate(1);
|
|
316
|
+
isProgrammaticallySeeked = false;
|
|
317
|
+
resolve();
|
|
318
|
+
}, (diff * 1000) / (playbackRate - 1), 20);
|
|
319
|
+
_playerInterface.setPlaybackRate(playbackRate);
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
_emitter.once('stalled', _handleFailed);
|
|
323
|
+
|
|
324
|
+
if(_playerInterface.isPaused) {
|
|
325
|
+
wasPaused = true;
|
|
326
|
+
_playerInterface.play()
|
|
327
|
+
.then(() => {
|
|
328
|
+
_fastForward();
|
|
329
|
+
})
|
|
330
|
+
.catch(_handleFailed);
|
|
331
|
+
} else {
|
|
332
|
+
_fastForward();
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
room._log(`Jump to seek...`);
|
|
337
|
+
let currentTimestamp = Date.now();
|
|
338
|
+
let __failsafeId = null
|
|
339
|
+
let _resolve = () => {
|
|
340
|
+
clearTimeout(__failsafeId);
|
|
341
|
+
__failsafeId = null;
|
|
342
|
+
isProgrammaticallySeeked = false;
|
|
343
|
+
room._log(`It took the player ${(Date.now() - currentTimestamp) / 1000} seconds to seek `);
|
|
344
|
+
resolve();
|
|
345
|
+
};
|
|
346
|
+
isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm
|
|
347
|
+
_emitter.once('playing', _resolve);
|
|
348
|
+
__failsafeId = setTimeout(_resolve, waitForPlayingEventAfterSeek);
|
|
349
|
+
_playerInterface.seek(time);
|
|
350
|
+
}
|
|
351
|
+
} else resolve()
|
|
352
|
+
});
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
const handlePlaying = () => {
|
|
356
|
+
if(!isProgrammaticallySeeked) {
|
|
357
|
+
room._log('Handle playing');
|
|
358
|
+
startSyncLoop();
|
|
359
|
+
}
|
|
360
|
+
isProgrammaticallySeeked = false;
|
|
361
|
+
isPreloading = _playerInterface.isPaused;
|
|
362
|
+
isPlaying = !_playerInterface.isPaused;
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
const handlePause = () => {
|
|
366
|
+
stopSyncLoop()
|
|
367
|
+
clientPaused().catch(() => {});
|
|
368
|
+
isPlaying = !_playerInterface.isPaused;
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
const handleBuffering = () => {
|
|
372
|
+
room._log('handleBuffering');
|
|
373
|
+
if(!isProgrammaticallySeeked) {
|
|
374
|
+
stopSyncLoop();
|
|
375
|
+
clientPaused().catch(() => {});
|
|
376
|
+
isPreloading = _playerInterface.isPaused;
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
const roomSyncSend = (slaveId) => {
|
|
381
|
+
|
|
382
|
+
if(!_playerInterface) {
|
|
383
|
+
room._log(
|
|
384
|
+
`I've been asked for position even if we don't have player attached.
|
|
385
|
+
Does it mean I'm the master?`
|
|
386
|
+
);
|
|
387
|
+
return Promise.resolve();
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
var fragmentData = _playerInterface?.currentFragment || {
|
|
391
|
+
fragment: String("0"),
|
|
392
|
+
fragment_pos: Number(parseInt(getCurrentPlayerPosition()))
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
room._log(`Sending my position to ${slaveId}`);
|
|
396
|
+
room._log(`Current time: ${fragmentData.fragment} # ${fragmentData.fragment_pos}`);
|
|
397
|
+
|
|
398
|
+
return room.sendMessage(room.handleId, {
|
|
399
|
+
body : {
|
|
400
|
+
request: "sync_response",
|
|
401
|
+
room: room.roomId,
|
|
402
|
+
timestamp: new Date().getTime(),
|
|
403
|
+
slave_id: room.webrtcVersion > 1000 ? String(slaveId) : Number(slaveId),
|
|
404
|
+
...fragmentData,
|
|
405
|
+
}});
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
const getSyncData = () => {
|
|
409
|
+
|
|
410
|
+
room._log('Sending roomSync request');
|
|
411
|
+
let roomId = room.roomId;
|
|
412
|
+
var fragmentData = _playerInterface?.currentFragment || {
|
|
413
|
+
fragment: String("0"),
|
|
414
|
+
fragment_pos: Number(parseInt(getCurrentPlayerPosition()))
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return new Promise((resolve, reject) => {
|
|
418
|
+
|
|
419
|
+
let now = new Date().getTime();
|
|
420
|
+
let ping = null;
|
|
421
|
+
|
|
422
|
+
let sid = setTimeout(() => {
|
|
423
|
+
room.off('data', fn, this);
|
|
424
|
+
reject('Timeout');
|
|
425
|
+
}, 3000);
|
|
426
|
+
|
|
427
|
+
let body = {
|
|
428
|
+
request: "sync",
|
|
429
|
+
room: roomId,
|
|
430
|
+
timestamp: new Date().getTime(),
|
|
431
|
+
...fragmentData
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
let fn = (msg) => {
|
|
435
|
+
|
|
436
|
+
if(msg.videoroom && ['sync', 'sync_response'].includes(msg.videoroom)) {
|
|
437
|
+
|
|
438
|
+
if(msg.sync_master_await) {
|
|
439
|
+
room._log('Waiting for master position');
|
|
440
|
+
if(!ping) {
|
|
441
|
+
ping = (new Date().getTime() - now);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
else if(msg.sync_master_fragment || msg.sync_master_fragment_pos) {
|
|
446
|
+
room._log('Got master position data');
|
|
447
|
+
if(!ping) {
|
|
448
|
+
ping = (new Date().getTime() - now);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
room._log(`I'm master: ${!!msg.sync_master_self}`);
|
|
452
|
+
room._log(`Ping: ${ping}`);
|
|
453
|
+
room._log(`Master fragment: ${msg.sync_master_fragment}`);
|
|
454
|
+
room._log(`Master fragment position: ${msg.sync_master_fragment_pos}`);
|
|
455
|
+
|
|
456
|
+
room.off('data', fn, this);
|
|
457
|
+
clearTimeout(sid);
|
|
458
|
+
|
|
459
|
+
resolve({
|
|
460
|
+
isMaster: !!msg.sync_master_self,
|
|
461
|
+
ping: ping,
|
|
462
|
+
masterFragmentPos: parseInt(msg.sync_master_fragment_pos),
|
|
463
|
+
masterFragmentSn: parseInt(msg.sync_master_fragment),
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
else {
|
|
468
|
+
clearTimeout(sid);
|
|
469
|
+
reject('Master lost connection')
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
room.on('data', fn, this);
|
|
474
|
+
room.sendMessage(room.handleId, {body}).then(fn).catch(e => {
|
|
475
|
+
room.off('data', fn, this);
|
|
476
|
+
clearTimeout(sid);
|
|
477
|
+
reject(e)
|
|
478
|
+
});
|
|
479
|
+
});
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
const clientPaused = () => {
|
|
483
|
+
room._log('Sending client paused');
|
|
484
|
+
|
|
485
|
+
if(!isConnected()) {
|
|
486
|
+
return Promise.resolve();
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return room.sendMessage(room.handleId, {
|
|
490
|
+
body:{
|
|
491
|
+
request: "sync_paused",
|
|
492
|
+
room: room.roomId,
|
|
493
|
+
timestamp: new Date().getTime(),
|
|
494
|
+
fragment:'0',
|
|
495
|
+
fragment_pos:0
|
|
496
|
+
}});
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
const propagateMasterFunc = () => {
|
|
500
|
+
room._log('Propagating master');
|
|
501
|
+
if(!isConnected()) {
|
|
502
|
+
return Promise.resolve();
|
|
503
|
+
}
|
|
504
|
+
return room.sendMessage(room.handleId, {
|
|
505
|
+
body:{
|
|
506
|
+
request: "sync_source_set",
|
|
507
|
+
room: room.roomId,
|
|
508
|
+
timestamp: new Date().getTime(),
|
|
509
|
+
wt_channel_id:"",
|
|
510
|
+
fragment:"0",
|
|
511
|
+
fragment_pos:0
|
|
512
|
+
}});
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
return {
|
|
516
|
+
|
|
517
|
+
__events: ['playerSyncing'],
|
|
518
|
+
|
|
519
|
+
initialize: (playerInterface) => {
|
|
520
|
+
|
|
521
|
+
_playerInterface = playerInterface;
|
|
522
|
+
_playerInterfaceOptions = {..._playerInterfaceOptions, ...(_playerInterface.syncSettings || {})}
|
|
523
|
+
|
|
524
|
+
room._log('Interface options passed: ', (_playerInterface.syncSettings || {}));
|
|
525
|
+
room._log('All interface options: ', _playerInterfaceOptions );
|
|
526
|
+
|
|
527
|
+
if(_playerInterfaceOptions.type === 'none') {
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if(_playerInterfaceOptions.type === 'push') {
|
|
532
|
+
room._log('Push sync is not implemented in this sync module yet');
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if(_playerInterface.isVod) {
|
|
537
|
+
room._log('VOD sync is not implemented in this sync module yet');
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
playerInterface.addHandlers({
|
|
542
|
+
handlePause: (event) => {
|
|
543
|
+
room._log('handlePause');
|
|
544
|
+
_emitter.emit('pause', event);
|
|
545
|
+
},
|
|
546
|
+
handlePlaying: (event) => {
|
|
547
|
+
room._log('handlePlaying');
|
|
548
|
+
_emitter.emit('playing', event);
|
|
549
|
+
},
|
|
550
|
+
handleBuffering: (event) => {
|
|
551
|
+
room._log('handleBuffering');
|
|
552
|
+
_emitter.emit('buffering', event);
|
|
553
|
+
_emitter.emit('stalled', event);
|
|
554
|
+
},
|
|
555
|
+
handleTimeupdate: (event) => {
|
|
556
|
+
room._log('handleTimeupdate');
|
|
557
|
+
_emitter.emit('timeupdate', event);
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
shouldPropagateMaster = false;
|
|
563
|
+
|
|
564
|
+
isPlaying = _playerInterface.isPaused === false;
|
|
565
|
+
|
|
566
|
+
room.on('disconnect', stopSyncLoop);
|
|
567
|
+
room.on('removeLocalParticipant', stopSyncLoop);
|
|
568
|
+
room.on('addLocalParticipant', handleAddLocalParticipant);
|
|
569
|
+
room.on('addRemoteParticipant', handleAddRemoteParticipant);
|
|
570
|
+
room.on('data', parseDataEvents);
|
|
571
|
+
|
|
572
|
+
if(shouldPropagateMaster) {
|
|
573
|
+
propagateMasterFunc().catch(() => {});
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if(_playerInterface.isPaused === false) {
|
|
577
|
+
_emitter.once('timeupdate', restartSyncLoop);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
_emitter.on('buffering', handleBuffering);
|
|
581
|
+
_emitter.on('playing', handlePlaying);
|
|
582
|
+
_emitter.on('pause', handlePause);
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
},
|
|
586
|
+
|
|
587
|
+
destroy: () => {
|
|
588
|
+
stopSyncLoop();
|
|
589
|
+
room.off('disconnect', stopSyncLoop);
|
|
590
|
+
room.off('removeLocalParticipant', stopSyncLoop);
|
|
591
|
+
room.off('addLocalParticipant', handleAddLocalParticipant);
|
|
592
|
+
room.off('addRemoteParticipant', handleAddRemoteParticipant);
|
|
593
|
+
room.off('data', parseDataEvents);
|
|
594
|
+
|
|
595
|
+
if(typeof _playerInterface?.destroy === 'function') {
|
|
596
|
+
_playerInterface?.destroy()
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
_playerInterface = null;
|
|
600
|
+
|
|
601
|
+
_emitter.clear();
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
export default syncModule;
|