senza-sdk 4.2.55-9808823.0 → 4.2.55-e39d067.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/dist/bundle.js +1 -1
- package/package.json +1 -1
- package/src/lifecycle.js +15 -6
- package/src/remotePlayer.js +376 -33
package/package.json
CHANGED
package/src/lifecycle.js
CHANGED
|
@@ -710,15 +710,24 @@ class Lifecycle extends EventTarget {
|
|
|
710
710
|
* Failure to process the moveToBackground command will result in the promise being rejected.
|
|
711
711
|
*/
|
|
712
712
|
moveToBackground() {
|
|
713
|
-
if (
|
|
714
|
-
if (
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
713
|
+
if (window.cefQuery) {
|
|
714
|
+
if (this._inTransition || this._state === this.UiState.BACKGROUND || this._state === this.UiState.IN_TRANSITION_TO_BACKGROUND) {
|
|
715
|
+
sdkLogger.warn(`lifecycle moveToBackground: No need to transition to background, state: ${this._state} transition: ${this._inTransition}`);
|
|
716
|
+
return Promise.resolve(false);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
if (remotePlayer._isSeekingByApplication) {
|
|
719
720
|
remotePlayer._targetSeekPlayingState = TargetPlayingState.PLAYING_ABR;
|
|
720
721
|
return Promise.resolve(true);
|
|
721
722
|
}
|
|
723
|
+
if (remotePlayer._isSetSubtitlesInProgress) {
|
|
724
|
+
remotePlayer._targetSetSubtitlePlayingState = TargetPlayingState.PLAYING_ABR;
|
|
725
|
+
return Promise.resolve(true);
|
|
726
|
+
}
|
|
727
|
+
if (remotePlayer._isSetAudioInProgress) {
|
|
728
|
+
remotePlayer._targetSetAudioPlayingState = TargetPlayingState.PLAYING_ABR;
|
|
729
|
+
return Promise.resolve(true);
|
|
730
|
+
}
|
|
722
731
|
}
|
|
723
732
|
return this._moveToBackground();
|
|
724
733
|
}
|
package/src/remotePlayer.js
CHANGED
|
@@ -694,7 +694,7 @@ class RemotePlayer extends EventTarget {
|
|
|
694
694
|
return Promise.resolve(undefined);
|
|
695
695
|
}
|
|
696
696
|
|
|
697
|
-
_play() {
|
|
697
|
+
_play(streamType) {
|
|
698
698
|
if (window.cefQuery) {
|
|
699
699
|
const FCID = getFCID();
|
|
700
700
|
const logger = sdkLogger.withFields({ FCID });
|
|
@@ -714,16 +714,10 @@ class RemotePlayer extends EventTarget {
|
|
|
714
714
|
playbackPosition: this.currentTime
|
|
715
715
|
};
|
|
716
716
|
let waitForResponse = false;
|
|
717
|
-
this._changePlayMode(true);
|
|
718
717
|
if (this._remotePlayerApiVersion >= 2) {
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
waitForResponse = true;
|
|
723
|
-
} else {
|
|
724
|
-
logger.log("remotePlayer play request ignored and will be sent with the lifecycle.moveToBackground()");
|
|
725
|
-
return Promise.resolve();
|
|
726
|
-
}
|
|
718
|
+
message.switchMode = this._isAudioSyncEnabled() ? SwitchMode.SEAMLESS : SwitchMode.NON_SEAMLESS;
|
|
719
|
+
message.streamType = streamType;
|
|
720
|
+
waitForResponse = true;
|
|
727
721
|
}
|
|
728
722
|
const request = { target: "TC", waitForResponse: waitForResponse, message: JSON.stringify(message) };
|
|
729
723
|
return new Promise((resolve, reject) => {
|
|
@@ -762,7 +756,6 @@ class RemotePlayer extends EventTarget {
|
|
|
762
756
|
|
|
763
757
|
_pause() {
|
|
764
758
|
if (window.cefQuery) {
|
|
765
|
-
this._changePlayMode(false);
|
|
766
759
|
const isForegroundState = lifecycle.state === lifecycle.UiState.FOREGROUND || lifecycle.state === lifecycle.UiState.IN_TRANSITION_TO_FOREGROUND;
|
|
767
760
|
if (this._remotePlayerApiVersion >= 2 && !this._isAudioSyncEnabled() && isForegroundState) {
|
|
768
761
|
sdkLogger.info("remotePlayer pause: application in foreground, remote player is not playing.");
|
|
@@ -816,6 +809,51 @@ class RemotePlayer extends EventTarget {
|
|
|
816
809
|
return Promise.resolve(undefined);
|
|
817
810
|
}
|
|
818
811
|
|
|
812
|
+
_stop(streamType) {
|
|
813
|
+
if (window.cefQuery) {
|
|
814
|
+
const FCID = getFCID();
|
|
815
|
+
const logger = sdkLogger.withFields({ FCID });
|
|
816
|
+
logger.log(`remotePlayer stop: sending stop action for streamType ${streamType}`);
|
|
817
|
+
const message = {
|
|
818
|
+
type: "remotePlayer.stop",
|
|
819
|
+
class: "remotePlayer",
|
|
820
|
+
action: "stop",
|
|
821
|
+
streamType: streamType,
|
|
822
|
+
fcid: FCID
|
|
823
|
+
};
|
|
824
|
+
const request = { target: "TC", waitForResponse: true, message: JSON.stringify(message) };
|
|
825
|
+
return new Promise((resolve, reject) => {
|
|
826
|
+
let timerId = 0;
|
|
827
|
+
const timeBeforeSendingRequest = Date.now();
|
|
828
|
+
const queryId = window.cefQuery({
|
|
829
|
+
request: JSON.stringify(request),
|
|
830
|
+
persistent: false,
|
|
831
|
+
onSuccess: () => {
|
|
832
|
+
const duration = Date.now() - timeBeforeSendingRequest;
|
|
833
|
+
logger.withFields({ duration }).log(`stop completed successfully after ${duration} ms`);
|
|
834
|
+
timerId = clearTimer(timerId);
|
|
835
|
+
resolve();
|
|
836
|
+
},
|
|
837
|
+
onFailure: (code, msg) => {
|
|
838
|
+
const duration = Date.now() - timeBeforeSendingRequest;
|
|
839
|
+
logger.withFields({ duration }).log(`stop failed after ${duration} ms. Error code: ${code}, error message: ${msg}`);
|
|
840
|
+
timerId = clearTimer(timerId);
|
|
841
|
+
reject(new RemotePlayerError(code, msg));
|
|
842
|
+
}
|
|
843
|
+
});
|
|
844
|
+
logger.log(`window.cefQuery for stop returned query id ${queryId}`);
|
|
845
|
+
const timeout = this._remotePlayerConfirmationTimeout + 1000;
|
|
846
|
+
timerId = setTimeout(() => {
|
|
847
|
+
logger.log(`stop reached timeout of ${timeout} ms, canceling query id ${queryId}`);
|
|
848
|
+
window.cefQueryCancel(queryId);
|
|
849
|
+
reject(new RemotePlayerError(6000, `stop reached timeout of ${timeout} ms`));
|
|
850
|
+
}, timeout, queryId);
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
sdkLogger.error("remotePlayer stop: window.cefQuery is undefined");
|
|
854
|
+
return Promise.resolve(undefined);
|
|
855
|
+
}
|
|
856
|
+
|
|
819
857
|
/** In order to support a seamless switch between the video in the UI and ABR, the web application must
|
|
820
858
|
* register the video element being used for the currently played video before calling the load and play apis.
|
|
821
859
|
* @param {object} video The video element currently playing video in the web application
|
|
@@ -891,6 +929,8 @@ class RemotePlayer extends EventTarget {
|
|
|
891
929
|
if (this._loadMode === this.LoadMode.LOADING || this._loadMode === this.LoadMode.UNLOADING) {
|
|
892
930
|
throw new RemotePlayerError(6501, "Cannot call load() while previous load/unload is still in progress");
|
|
893
931
|
}
|
|
932
|
+
this._abortSetAudioLanguage = true;
|
|
933
|
+
this._abortSetSubtitleLanguage = true;
|
|
894
934
|
this._abortSeeking = true;
|
|
895
935
|
if (reset) {
|
|
896
936
|
this._reset();
|
|
@@ -1064,6 +1104,20 @@ class RemotePlayer extends EventTarget {
|
|
|
1064
1104
|
}
|
|
1065
1105
|
}
|
|
1066
1106
|
|
|
1107
|
+
this._changePlayMode(true);
|
|
1108
|
+
|
|
1109
|
+
if (this._isSetAudioInProgress) {
|
|
1110
|
+
sdkLogger.info("application requesting play during setAudioLanguage");
|
|
1111
|
+
this._targetSetAudioPlayingState = TargetPlayingState.PLAYING_UI;
|
|
1112
|
+
return Promise.resolve(true);
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
if (this._isSetSubtitlesInProgress) {
|
|
1116
|
+
sdkLogger.info("application requesting play during setSubtitleLanguage");
|
|
1117
|
+
this._targetSetSubtitlePlayingState = TargetPlayingState.PLAYING_UI;
|
|
1118
|
+
return Promise.resolve(true);
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1067
1121
|
// If seeking in progress, wait for seek to complete before playing
|
|
1068
1122
|
if (this._isSeekingByApplication) {
|
|
1069
1123
|
sdkLogger.info("application requesting play during seek");
|
|
@@ -1078,7 +1132,13 @@ class RemotePlayer extends EventTarget {
|
|
|
1078
1132
|
this._seek(this._videoElement.currentTime, false);
|
|
1079
1133
|
}
|
|
1080
1134
|
}*/
|
|
1081
|
-
|
|
1135
|
+
|
|
1136
|
+
if (this._remotePlayerApiVersion >= 2 && !this._isAudioSyncEnabled()) {
|
|
1137
|
+
sdkLogger.info("play was called but _isAudioSyncEnabled is disabled, ignoring.");
|
|
1138
|
+
return Promise.resolve(true);
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
return this._play(StreamType.AUDIO);
|
|
1082
1142
|
}
|
|
1083
1143
|
|
|
1084
1144
|
/** Pauses the currently playing audio or video
|
|
@@ -1098,11 +1158,24 @@ class RemotePlayer extends EventTarget {
|
|
|
1098
1158
|
throw new RemotePlayerError(6001, "Cannot call pause() if player is not loaded");
|
|
1099
1159
|
}
|
|
1100
1160
|
}
|
|
1161
|
+
|
|
1162
|
+
this._changePlayMode(false);
|
|
1163
|
+
|
|
1101
1164
|
if (this._isSeekingByApplication) {
|
|
1102
1165
|
sdkLogger.info("application requesting pause during seek");
|
|
1103
1166
|
this._targetSeekPlayingState = TargetPlayingState.PAUSED;
|
|
1104
1167
|
return Promise.resolve(true);
|
|
1105
1168
|
}
|
|
1169
|
+
if (this._isSetAudioInProgress) {
|
|
1170
|
+
sdkLogger.info("application requesting pause during setAudioLanguage");
|
|
1171
|
+
this._targetSetAudioPlayingState = TargetPlayingState.PAUSED;
|
|
1172
|
+
return Promise.resolve(true);
|
|
1173
|
+
}
|
|
1174
|
+
if (this._isSetSubtitlesInProgress) {
|
|
1175
|
+
sdkLogger.info("application requesting pause during setSubtitleLanguage");
|
|
1176
|
+
this._targetSetSubtitlePlayingState = TargetPlayingState.PAUSED;
|
|
1177
|
+
return Promise.resolve(true);
|
|
1178
|
+
}
|
|
1106
1179
|
return this._pause();
|
|
1107
1180
|
}
|
|
1108
1181
|
|
|
@@ -1190,7 +1263,6 @@ class RemotePlayer extends EventTarget {
|
|
|
1190
1263
|
const prevSelectedAudioTrack = this._selectedAudioTrack;
|
|
1191
1264
|
for (const track of this.getAudioTracks()) {
|
|
1192
1265
|
if (track.id === audioTrackId) {
|
|
1193
|
-
this._selectedAudioTrack = audioTrackId;
|
|
1194
1266
|
found = true;
|
|
1195
1267
|
break;
|
|
1196
1268
|
}
|
|
@@ -1199,19 +1271,33 @@ class RemotePlayer extends EventTarget {
|
|
|
1199
1271
|
sdkLogger.warn(`Invalid audioTrackId ${audioTrackId}`);
|
|
1200
1272
|
return Promise.resolve();
|
|
1201
1273
|
}
|
|
1202
|
-
if (this.
|
|
1203
|
-
return Promise.resolve(); // Resolve immediately for older versions
|
|
1204
|
-
}
|
|
1205
|
-
if (prevSelectedAudioTrack === this._selectedAudioTrack) {
|
|
1274
|
+
if (this._selectedAudioTrack === audioTrackId) {
|
|
1206
1275
|
return Promise.resolve(); // Audio language already selected
|
|
1207
1276
|
}
|
|
1208
1277
|
|
|
1209
|
-
|
|
1278
|
+
switch (this._remotePlayerApiVersion) {
|
|
1279
|
+
case 0:
|
|
1280
|
+
case 1:
|
|
1281
|
+
this._selectedAudioTrack = audioTrackId;
|
|
1282
|
+
return Promise.resolve(); // Resolve immediately for older versions
|
|
1283
|
+
case 2:
|
|
1284
|
+
return this._selectAudioTrackV2(audioTrackId, prevSelectedAudioTrack);
|
|
1285
|
+
default:
|
|
1286
|
+
return this._atomicSetAudioLanguage(audioTrackId, prevSelectedAudioTrack);
|
|
1287
|
+
}
|
|
1210
1288
|
}
|
|
1211
1289
|
|
|
1212
|
-
|
|
1290
|
+
/**
|
|
1291
|
+
* Handles the asynchronous selection of an audio track.
|
|
1292
|
+
* If the player is playing, it pauses before changing the track and resumes playback if necessary.
|
|
1293
|
+
*
|
|
1294
|
+
* @param {string} audioTrackId - The ID of the audio track to select.
|
|
1295
|
+
* @param {string} prevSelectedAudioTrack - The previously selected audio track ID.
|
|
1296
|
+
* @returns {Promise<void>} Resolves when the operation is complete.
|
|
1297
|
+
* */
|
|
1298
|
+
async _selectAudioTrackV2(audioTrackId, prevSelectedAudioTrack) {
|
|
1213
1299
|
const prevIsPlaying = this._isPlaying;
|
|
1214
|
-
sdkLogger.log(`remotePlayer
|
|
1300
|
+
sdkLogger.log(`remotePlayer _selectAudioTrackV2: prevAudioTrack=${prevSelectedAudioTrack} audioTrackId=${audioTrackId} isPlaying=${this._isPlaying}`);
|
|
1215
1301
|
try {
|
|
1216
1302
|
if (this._isPlaying) await this.pause();
|
|
1217
1303
|
let position = this.currentTime;
|
|
@@ -1219,9 +1305,9 @@ class RemotePlayer extends EventTarget {
|
|
|
1219
1305
|
position = this._videoElement.currentTime;
|
|
1220
1306
|
}
|
|
1221
1307
|
await this._load(this._loadedUrl, position, audioTrackId, undefined, false);
|
|
1308
|
+
this._selectedAudioTrack = audioTrackId;
|
|
1222
1309
|
} catch (e) {
|
|
1223
|
-
// Do NOT reject - just log
|
|
1224
|
-
this._selectedAudioTrack = prevSelectedAudioTrack;
|
|
1310
|
+
// Do NOT reject - just log
|
|
1225
1311
|
sdkLogger.warn(`Failed to select audio track ${audioTrackId}: ${e.message}`);
|
|
1226
1312
|
return;
|
|
1227
1313
|
}
|
|
@@ -1238,6 +1324,62 @@ class RemotePlayer extends EventTarget {
|
|
|
1238
1324
|
}
|
|
1239
1325
|
}
|
|
1240
1326
|
|
|
1327
|
+
/**
|
|
1328
|
+
* Handles the asynchronous selection of an audio track.
|
|
1329
|
+
* If the player is playing, it stops audio streamType before changing the track and resumes audio streamType playback if necessary.
|
|
1330
|
+
* Available only from v3 and on
|
|
1331
|
+
*
|
|
1332
|
+
* @param {string} audioTrackId - The ID of the audio track to select.
|
|
1333
|
+
* @returns {Promise<void>} Resolves when the operation is complete.
|
|
1334
|
+
* */
|
|
1335
|
+
async _selectAudioTrackV3(audioTrackId) {
|
|
1336
|
+
sdkLogger.log(`remotePlayer _selectAudioTrackV3: audioTrackId=${audioTrackId} isPlaying=${this._isPlaying}`);
|
|
1337
|
+
if (window.cefQuery) {
|
|
1338
|
+
const FCID = getFCID();
|
|
1339
|
+
const logger = sdkLogger.withFields({ FCID });
|
|
1340
|
+
logger.log("remotePlayer _selectAudioTrackV3: sending setAudioLanguage action");
|
|
1341
|
+
const message = {
|
|
1342
|
+
type: "remotePlayer.setAudioLanguage",
|
|
1343
|
+
class: "remotePlayer",
|
|
1344
|
+
action: "setAudioLanguage",
|
|
1345
|
+
fcid: FCID,
|
|
1346
|
+
language: audioTrackId
|
|
1347
|
+
};
|
|
1348
|
+
const request = { target: "TC", waitForResponse: true, message: JSON.stringify(message) };
|
|
1349
|
+
return new Promise((resolve, reject) => {
|
|
1350
|
+
let timerId = 0;
|
|
1351
|
+
const timeBeforeSendingRequest = Date.now();
|
|
1352
|
+
const queryId = window.cefQuery({
|
|
1353
|
+
request: JSON.stringify(request),
|
|
1354
|
+
persistent: false,
|
|
1355
|
+
onSuccess: () => {
|
|
1356
|
+
this._selectedAudioTrack = audioTrackId;
|
|
1357
|
+
const duration = Date.now() - timeBeforeSendingRequest;
|
|
1358
|
+
logger.withFields({ duration }).log(`setAudioLanguage completed successfully after ${duration} ms`);
|
|
1359
|
+
timerId = clearTimer(timerId);
|
|
1360
|
+
resolve();
|
|
1361
|
+
},
|
|
1362
|
+
onFailure: (code, msg) => {
|
|
1363
|
+
const duration = Date.now() - timeBeforeSendingRequest;
|
|
1364
|
+
logger.withFields({ duration }).log(`setAudioLanguage failed after ${duration} ms. Error code: ${code}, error message: ${msg}`);
|
|
1365
|
+
timerId = clearTimer(timerId);
|
|
1366
|
+
reject(new RemotePlayerError(code, msg));
|
|
1367
|
+
}
|
|
1368
|
+
});
|
|
1369
|
+
logger.log(`window.cefQuery for setAudioLanguage returned query id ${queryId}`);
|
|
1370
|
+
const timeout = this._remotePlayerConfirmationTimeout + 1000;
|
|
1371
|
+
timerId = setTimeout(() => {
|
|
1372
|
+
logger.log(`setAudioLanguage reached timeout of ${timeout} ms, canceling query id ${queryId}`);
|
|
1373
|
+
window.cefQueryCancel(queryId);
|
|
1374
|
+
reject(new RemotePlayerError(6000, `setAudioLanguage reached timeout of ${timeout} ms`));
|
|
1375
|
+
}, timeout, queryId);
|
|
1376
|
+
});
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
sdkLogger.error("remotePlayer _selectAudioTrackV3: window.cefQuery is undefined");
|
|
1380
|
+
return Promise.resolve(undefined);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1241
1383
|
/** Select a specific text (subtitle) track.
|
|
1242
1384
|
* Track id should come from a call to getTextTracks.
|
|
1243
1385
|
* If no tracks exist - this is a no-op.
|
|
@@ -1257,7 +1399,6 @@ class RemotePlayer extends EventTarget {
|
|
|
1257
1399
|
const prevSelectedTextTrack = this._selectedSubtitlesTrack;
|
|
1258
1400
|
for (const track of this.getTextTracks()) {
|
|
1259
1401
|
if (track.id === textTrackId) {
|
|
1260
|
-
this._selectedSubtitlesTrack = textTrackId;
|
|
1261
1402
|
found = true;
|
|
1262
1403
|
break;
|
|
1263
1404
|
}
|
|
@@ -1266,14 +1407,20 @@ class RemotePlayer extends EventTarget {
|
|
|
1266
1407
|
sdkLogger.warn(`Invalid textTrackId ${textTrackId}`);
|
|
1267
1408
|
return Promise.resolve();
|
|
1268
1409
|
}
|
|
1269
|
-
if (this.
|
|
1270
|
-
return Promise.resolve(); // Resolve immediately for older versions
|
|
1271
|
-
}
|
|
1272
|
-
if (prevSelectedTextTrack === this._selectedSubtitlesTrack) {
|
|
1410
|
+
if (this._selectedSubtitlesTrack === textTrackId) {
|
|
1273
1411
|
return Promise.resolve(); // Subtitle language already selected
|
|
1274
1412
|
}
|
|
1275
1413
|
|
|
1276
|
-
|
|
1414
|
+
switch (this._remotePlayerApiVersion) {
|
|
1415
|
+
case 0:
|
|
1416
|
+
case 1:
|
|
1417
|
+
this._selectedSubtitlesTrack = textTrackId;
|
|
1418
|
+
return Promise.resolve(); // Resolve immediately for older versions
|
|
1419
|
+
case 2:
|
|
1420
|
+
return this._selectTextTrackV2(textTrackId, prevSelectedTextTrack);
|
|
1421
|
+
default:
|
|
1422
|
+
return this._atomicSetSubtitleLanguage(textTrackId, prevSelectedTextTrack);
|
|
1423
|
+
}
|
|
1277
1424
|
}
|
|
1278
1425
|
|
|
1279
1426
|
/**
|
|
@@ -1284,9 +1431,9 @@ class RemotePlayer extends EventTarget {
|
|
|
1284
1431
|
* @param {string} prevSelectedTextTrack - The previously selected text track ID.
|
|
1285
1432
|
* @returns {Promise<void>} Resolves when the operation is complete.
|
|
1286
1433
|
* */
|
|
1287
|
-
async
|
|
1434
|
+
async _selectTextTrackV2(textTrackId, prevSelectedTextTrack) {
|
|
1288
1435
|
const prevIsPlaying = this._isPlaying;
|
|
1289
|
-
sdkLogger.log(`remotePlayer
|
|
1436
|
+
sdkLogger.log(`remotePlayer _selectTextTrackV2: prevTextTrack=${prevSelectedTextTrack} textTrackId=${textTrackId} isPlaying=${this._isPlaying}`);
|
|
1290
1437
|
try {
|
|
1291
1438
|
if (this._isPlaying) await this.pause();
|
|
1292
1439
|
let position = this.currentTime;
|
|
@@ -1294,9 +1441,9 @@ class RemotePlayer extends EventTarget {
|
|
|
1294
1441
|
position = this._videoElement.currentTime;
|
|
1295
1442
|
}
|
|
1296
1443
|
await this._load(this._loadedUrl, position, undefined, textTrackId, false);
|
|
1444
|
+
this._selectedSubtitlesTrack = textTrackId;
|
|
1297
1445
|
} catch (e) {
|
|
1298
|
-
// Do NOT reject - just log
|
|
1299
|
-
this._selectedSubtitlesTrack = prevSelectedTextTrack;
|
|
1446
|
+
// Do NOT reject - just log
|
|
1300
1447
|
sdkLogger.warn(`Failed to select text track ${textTrackId}: ${e.message}`);
|
|
1301
1448
|
return;
|
|
1302
1449
|
}
|
|
@@ -1313,6 +1460,60 @@ class RemotePlayer extends EventTarget {
|
|
|
1313
1460
|
}
|
|
1314
1461
|
}
|
|
1315
1462
|
|
|
1463
|
+
/**
|
|
1464
|
+
* Handles the asynchronous selection of a text track.
|
|
1465
|
+
* If the player is playing, it stops subtitle streamType before changing the track and resumes subtitle streamType playback if necessary.
|
|
1466
|
+
* Available only from v3 and on
|
|
1467
|
+
*
|
|
1468
|
+
* @param {string} textTrackId - The ID of the text track to select.
|
|
1469
|
+
* @returns {Promise<void>} Resolves when the operation is complete.
|
|
1470
|
+
* */
|
|
1471
|
+
async _selectTextTrackV3(textTrackId) {
|
|
1472
|
+
if (window.cefQuery) {
|
|
1473
|
+
const FCID = getFCID();
|
|
1474
|
+
const logger = sdkLogger.withFields({ FCID });
|
|
1475
|
+
logger.log("remotePlayer _selectTextTrackV3: sending setSubtitleLanguage action");
|
|
1476
|
+
const message = {
|
|
1477
|
+
type: "remotePlayer.setSubtitleLanguage",
|
|
1478
|
+
class: "remotePlayer",
|
|
1479
|
+
action: "setSubtitleLanguage",
|
|
1480
|
+
fcid: FCID,
|
|
1481
|
+
language: textTrackId
|
|
1482
|
+
};
|
|
1483
|
+
const request = { target: "TC", waitForResponse: true, message: JSON.stringify(message) };
|
|
1484
|
+
return new Promise((resolve, reject) => {
|
|
1485
|
+
let timerId = 0;
|
|
1486
|
+
const timeBeforeSendingRequest = Date.now();
|
|
1487
|
+
const queryId = window.cefQuery({
|
|
1488
|
+
request: JSON.stringify(request),
|
|
1489
|
+
persistent: false,
|
|
1490
|
+
onSuccess: () => {
|
|
1491
|
+
this._selectedSubtitlesTrack = textTrackId;
|
|
1492
|
+
const duration = Date.now() - timeBeforeSendingRequest;
|
|
1493
|
+
logger.withFields({ duration }).log(`setSubtitleLanguage completed successfully after ${duration} ms`);
|
|
1494
|
+
timerId = clearTimer(timerId);
|
|
1495
|
+
resolve();
|
|
1496
|
+
},
|
|
1497
|
+
onFailure: (code, msg) => {
|
|
1498
|
+
const duration = Date.now() - timeBeforeSendingRequest;
|
|
1499
|
+
logger.withFields({ duration }).log(`setSubtitleLanguage failed after ${duration} ms. Error code: ${code}, error message: ${msg}`);
|
|
1500
|
+
timerId = clearTimer(timerId);
|
|
1501
|
+
reject(new RemotePlayerError(code, msg));
|
|
1502
|
+
}
|
|
1503
|
+
});
|
|
1504
|
+
logger.log(`window.cefQuery for setSubtitleLanguage returned query id ${queryId}`);
|
|
1505
|
+
const timeout = this._remotePlayerConfirmationTimeout + 1000;
|
|
1506
|
+
timerId = setTimeout(() => {
|
|
1507
|
+
logger.log(`setSubtitleLanguage reached timeout of ${timeout} ms, canceling query id ${queryId}`);
|
|
1508
|
+
window.cefQueryCancel(queryId);
|
|
1509
|
+
reject(new RemotePlayerError(6000, `setSubtitleLanguage reached timeout of ${timeout} ms`));
|
|
1510
|
+
}, timeout, queryId);
|
|
1511
|
+
});
|
|
1512
|
+
}
|
|
1513
|
+
sdkLogger.error("remotePlayer _selectTextTrackV3: window.cefQuery is undefined");
|
|
1514
|
+
return Promise.resolve(undefined);
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1316
1517
|
/**
|
|
1317
1518
|
* Enable or disable the subtitles.
|
|
1318
1519
|
* If the player is in an unloaded state, the request will be applied next time content is played.
|
|
@@ -1423,6 +1624,11 @@ class RemotePlayer extends EventTarget {
|
|
|
1423
1624
|
* @private
|
|
1424
1625
|
*/
|
|
1425
1626
|
async _startSeeking(playbackPosition) {
|
|
1627
|
+
if (this._isSetAudioInProgress || this._isSetSubtitlesInProgress) {
|
|
1628
|
+
sdkLogger.info(`Seeking not supported while setAudioLanguage or setSubtitleLanguage are in progress.`);
|
|
1629
|
+
return;
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1426
1632
|
if (!this._isSeekingByPlatform) {
|
|
1427
1633
|
this._pendingSeekPosition = playbackPosition;
|
|
1428
1634
|
|
|
@@ -1534,7 +1740,11 @@ class RemotePlayer extends EventTarget {
|
|
|
1534
1740
|
// In case where we aborted, we don't want to resume playback.
|
|
1535
1741
|
if (!this._abortSeeking) {
|
|
1536
1742
|
if (this._targetSeekPlayingState === TargetPlayingState.PLAYING_UI) {
|
|
1537
|
-
this.
|
|
1743
|
+
if (!this._isAudioSyncEnabled()) {
|
|
1744
|
+
return Promise.resolve(true);
|
|
1745
|
+
}
|
|
1746
|
+
// resume audio play only if _isAudioSyncEnabled
|
|
1747
|
+
this._play(StreamType.AUDIO);
|
|
1538
1748
|
} else if (this._targetSeekPlayingState === TargetPlayingState.PLAYING_ABR) {
|
|
1539
1749
|
lifecycle._moveToBackground();
|
|
1540
1750
|
}
|
|
@@ -1543,6 +1753,139 @@ class RemotePlayer extends EventTarget {
|
|
|
1543
1753
|
this._isSeekingByApplication = false;
|
|
1544
1754
|
sdkLogger.info("Seeking: local video element seeking end");
|
|
1545
1755
|
}
|
|
1756
|
+
|
|
1757
|
+
async _atomicSetAudioLanguage(audioTrackId, prevSelectedAudioTrack) {
|
|
1758
|
+
if (this._isSetAudioInProgress) {
|
|
1759
|
+
sdkLogger.warn(`remotePlayer _atomicSetAudioLanguage: audioTrackId=${audioTrackId} already in progress, ignoring.`);
|
|
1760
|
+
return Promise.resolve();
|
|
1761
|
+
}
|
|
1762
|
+
if (this._isSeekingByApplication) {
|
|
1763
|
+
sdkLogger.warn(`remotePlayer _atomicSetAudioLanguage: audioTrackId=${audioTrackId} ignored. seeking is already in progress.`);
|
|
1764
|
+
return Promise.resolve();
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
this._targetSetAudioPlayingState = this._isPlaying ? TargetPlayingState.PLAYING_UI : TargetPlayingState.PAUSED;
|
|
1768
|
+
sdkLogger.log(`remotePlayer _atomicSetAudioLanguage: prevAudioTrack=${prevSelectedAudioTrack} audioTrackId=${audioTrackId} isPlaying=${this._isPlaying} targetState=${this._targetSetAudioPlayingState}`);
|
|
1769
|
+
|
|
1770
|
+
this._abortSetAudioLanguage = false;
|
|
1771
|
+
this._isSetAudioInProgress = true;
|
|
1772
|
+
|
|
1773
|
+
try {
|
|
1774
|
+
await this._stop(StreamType.AUDIO);
|
|
1775
|
+
if (this._abortSetAudioLanguage) {
|
|
1776
|
+
this._isSetAudioInProgress = false;
|
|
1777
|
+
sdkLogger.warn(`remotePlayer _atomicSetAudioLanguage: audioTrackId=${audioTrackId} aborted.`);
|
|
1778
|
+
return Promise.resolve();
|
|
1779
|
+
}
|
|
1780
|
+
} catch (error) {
|
|
1781
|
+
this._isSetAudioInProgress = false;
|
|
1782
|
+
sdkLogger.warn(`remotePlayer _atomicSetAudioLanguage: audioTrackId=${audioTrackId} failed on stop. aborting.`);
|
|
1783
|
+
return Promise.reject(error);
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
let setLanguageError;
|
|
1787
|
+
try {
|
|
1788
|
+
await this._selectAudioTrackV3(audioTrackId);
|
|
1789
|
+
} catch (error) {
|
|
1790
|
+
sdkLogger.error(`remotePlayer _atomicSetAudioLanguage: audioTrackId=${audioTrackId} failed with error ${error.message}.`);
|
|
1791
|
+
if (!this._abortSetAudioLanguage) {
|
|
1792
|
+
setLanguageError = error;
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
// check if load/unload were called
|
|
1797
|
+
if (this._abortSetAudioLanguage) {
|
|
1798
|
+
this._isSetAudioInProgress = false;
|
|
1799
|
+
sdkLogger.warn(`remotePlayer _atomicSetAudioLanguage: audioTrackId=${audioTrackId} aborted.`);
|
|
1800
|
+
return Promise.resolve();
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
try {
|
|
1804
|
+
if (this._targetSetAudioPlayingState === TargetPlayingState.PLAYING_UI) {
|
|
1805
|
+
if (!this._isAudioSyncEnabled()) {
|
|
1806
|
+
this._isSetAudioInProgress = false;
|
|
1807
|
+
return Promise.resolve(true);
|
|
1808
|
+
}
|
|
1809
|
+
// resume audio play only if _isAudioSyncEnabled
|
|
1810
|
+
await this._play(StreamType.AUDIO);
|
|
1811
|
+
} else if (this._targetSetAudioPlayingState === TargetPlayingState.PLAYING_ABR) {
|
|
1812
|
+
await lifecycle._moveToBackground();
|
|
1813
|
+
}
|
|
1814
|
+
} catch(error) {
|
|
1815
|
+
sdkLogger.error(`remotePlayer _atomicSetAudioLanguage: audioTrackId=${audioTrackId} play failed with error ${error.message}.`);
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
this._isSetAudioInProgress = false;
|
|
1819
|
+
sdkLogger.log(`remotePlayer _atomicSetAudioLanguage: audioTrackId=${audioTrackId} ended.`);
|
|
1820
|
+
return setLanguageError ? Promise.reject(setLanguageError) : Promise.resolve();
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
async _atomicSetSubtitleLanguage(textTrackId, prevSelectedTextTrack) {
|
|
1824
|
+
if (this._isSetSubtitlesInProgress) {
|
|
1825
|
+
sdkLogger.warn(`remotePlayer _atomicSetSubtitleLanguage: textTrackId=${textTrackId} already in progress., ignoring.`);
|
|
1826
|
+
return Promise.resolve();
|
|
1827
|
+
}
|
|
1828
|
+
if (this._isSeekingByApplication) {
|
|
1829
|
+
sdkLogger.warn(`remotePlayer _atomicSetSubtitleLanguage: textTrackId=${textTrackId} ignored. seeking is already in progress.`);
|
|
1830
|
+
return Promise.resolve();
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
this._targetSetSubtitlePlayingState = this._isPlaying ? TargetPlayingState.PLAYING_UI : TargetPlayingState.PAUSED;
|
|
1834
|
+
sdkLogger.log(`remotePlayer _atomicSetSubtitleLanguage: prevTextTrack=${prevSelectedTextTrack} textTrackId=${textTrackId} isPlaying=${this._isPlaying} targetState=${this._targetSetSubtitlePlayingState}`);
|
|
1835
|
+
|
|
1836
|
+
this._abortSetSubtitleLanguage = false;
|
|
1837
|
+
this._isSetSubtitlesInProgress = true;
|
|
1838
|
+
|
|
1839
|
+
try {
|
|
1840
|
+
await this._stop(StreamType.SUBTITLE);
|
|
1841
|
+
if (this._abortSetSubtitleLanguage) {
|
|
1842
|
+
this._isSetSubtitlesInProgress = false;
|
|
1843
|
+
sdkLogger.warn(`remotePlayer _atomicSetSubtitleLanguage: textTrackId=${textTrackId} aborted.`);
|
|
1844
|
+
return Promise.resolve();
|
|
1845
|
+
}
|
|
1846
|
+
} catch (error) {
|
|
1847
|
+
this._isSetSubtitlesInProgress = false;
|
|
1848
|
+
sdkLogger.warn(`remotePlayer _atomicSetSubtitleLanguage: textTrackId=${textTrackId} failed on stop. aborting.`);
|
|
1849
|
+
return Promise.reject(error);
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
let setLanguageError;
|
|
1853
|
+
try {
|
|
1854
|
+
await this._selectTextTrackV3(textTrackId);
|
|
1855
|
+
} catch (error) {
|
|
1856
|
+
sdkLogger.error(`remotePlayer _atomicSetSubtitleLanguage: textTrackId=${textTrackId} failed with error ${error.message}.`);
|
|
1857
|
+
if (!this._abortSetSubtitleLanguage) {
|
|
1858
|
+
setLanguageError = error;
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
// check if load/unload were called
|
|
1863
|
+
if (this._abortSetSubtitleLanguage) {
|
|
1864
|
+
this._isSetSubtitlesInProgress = false;
|
|
1865
|
+
sdkLogger.warn(`remotePlayer _atomicSetSubtitleLanguage: textTrackId=${textTrackId} aborted.`);
|
|
1866
|
+
return Promise.resolve();
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
try {
|
|
1870
|
+
if (this._targetSetSubtitlePlayingState === TargetPlayingState.PLAYING_UI) {
|
|
1871
|
+
if (!this._isAudioSyncEnabled()) {
|
|
1872
|
+
this._isSetSubtitlesInProgress = false;
|
|
1873
|
+
return Promise.resolve(true);
|
|
1874
|
+
}
|
|
1875
|
+
// resume audio play only if _isAudioSyncEnabled
|
|
1876
|
+
await this._play(StreamType.AUDIO);
|
|
1877
|
+
} else if (this._targetSetSubtitlePlayingState === TargetPlayingState.PLAYING_ABR) {
|
|
1878
|
+
await lifecycle._moveToBackground();
|
|
1879
|
+
}
|
|
1880
|
+
} catch (error) {
|
|
1881
|
+
sdkLogger.error(`remotePlayer _atomicSetSubtitleLanguage: textTrackId=${textTrackId} play failed with error ${error.message}.`);
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
this._isSetSubtitlesInProgress = false;
|
|
1885
|
+
sdkLogger.log(`remotePlayer _atomicSetSubtitleLanguage: textTrackId=${textTrackId} ended.`);
|
|
1886
|
+
return setLanguageError ? Promise.reject(setLanguageError) : Promise.resolve();
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1546
1889
|
}
|
|
1547
1890
|
/**
|
|
1548
1891
|
*
|