senza-sdk 4.1.5 → 4.2.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.
@@ -0,0 +1,53 @@
1
+ /*
2
+ @license
3
+ Copyright 2006 The Closure Library Authors
4
+ SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ /*
8
+ @license
9
+ Copyright 2008 The Closure Library Authors
10
+ SPDX-License-Identifier: Apache-2.0
11
+ */
12
+
13
+ /*
14
+ @license
15
+ EME Encryption Scheme Polyfill
16
+ Copyright 2019 Google LLC
17
+ SPDX-License-Identifier: Apache-2.0
18
+ */
19
+
20
+ /*
21
+ @license
22
+ MSS Transmuxer
23
+ Copyright 2015 Dash Industry Forum
24
+ SPDX-License-Identifier: BSD-3-Clause
25
+ */
26
+
27
+ /*
28
+ @license
29
+ Shaka Player
30
+ Copyright 2016 Google LLC
31
+ SPDX-License-Identifier: Apache-2.0
32
+ */
33
+
34
+ /*
35
+ @license
36
+ Shaka Player
37
+ Copyright 2022 Google LLC
38
+ SPDX-License-Identifier: Apache-2.0
39
+ */
40
+
41
+ /*
42
+ @license
43
+ Shaka Player
44
+ Copyright 2023 Google LLC
45
+ SPDX-License-Identifier: Apache-2.0
46
+ */
47
+
48
+ /*
49
+ @license
50
+ tXml
51
+ Copyright 2015 Tobias Nickel
52
+ SPDX-License-Identifier: MIT
53
+ */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "senza-sdk",
3
- "version": "4.1.5",
3
+ "version": "4.2.0",
4
4
  "main": "./src/api.js",
5
5
  "description": "API for Senza application",
6
6
  "license": "MIT",
@@ -26,8 +26,8 @@
26
26
  "eslint-plugin-jest": "^26.1.1",
27
27
  "jest": "^27.5.1",
28
28
  "jsdoc-to-markdown": "^7.1.1",
29
- "webpack-cli": "^5.1.4",
30
- "webpack": "^5.72.1"
29
+ "webpack": "^5.72.1",
30
+ "webpack-cli": "^5.1.4"
31
31
  },
32
32
  "jest": {
33
33
  "verbose": false,
@@ -44,5 +44,8 @@
44
44
  "statements": 92
45
45
  }
46
46
  }
47
+ },
48
+ "dependencies": {
49
+ "shaka-player": "^4.12.5"
47
50
  }
48
51
  }
package/src/api.js CHANGED
@@ -384,6 +384,9 @@ export { platformManager } from "./platformManager";
384
384
  export { alarmManager } from "./alarmManager";
385
385
  export { messageManager } from "./messageManager";
386
386
  export { initSequence, showSequence } from "./devSequence";
387
+ export {SenzaShakaPlayer as ShakaPlayer} from "./senzaShakaPlayer";
388
+
389
+
387
390
  import "./devHelper";
388
391
 
389
392
  /**
package/src/devHelper.js CHANGED
@@ -1,26 +1,17 @@
1
- import { lifecycle,remotePlayer } from "./api";
1
+ import { lifecycle, remotePlayer } from "./api";
2
2
  import { sdkLogger } from "./utils";
3
3
 
4
4
  const timeout = 500;
5
5
  let timerId;
6
- let currentState;
7
-
8
- lifecycle.getState()?.then((state) => {
9
- currentState = state;
10
- });
11
-
12
- lifecycle.addEventListener("onstatechange", (e) => {
13
- currentState = e.state;
14
- });
15
6
 
16
7
  const handleHintLogging = (event) => {
17
- if (currentState === "background" || currentState === "inTransitionToBackground") {
8
+ if (lifecycle.state === lifecycle.UiState.BACKGROUND || lifecycle.state === lifecycle.UiState.IN_TRANSITION_TO_BACKGROUND) {
18
9
  if (timerId) {
19
10
  clearTimeout(timerId);
20
11
  }
21
12
  timerId = setTimeout(() => {
22
- if (currentState === "background" || currentState === "inTransitionToBackground") {
23
- sdkLogger.log(`${event.type} event received while in '${currentState}' state and there was no call to lifecycle.moveToForeground() for ${timeout} ms. Make sure to call it if you want to move to foreground.`);
13
+ if (lifecycle.state === lifecycle.UiState.BACKGROUND || lifecycle.state === lifecycle.UiState.IN_TRANSITION_TO_BACKGROUND) {
14
+ sdkLogger.log(`${event.type} event received while in '${lifecycle.state}' state and there was no call to lifecycle.moveToForeground() for ${timeout} ms. Make sure to call it if you want to move to foreground.`);
24
15
  }
25
16
  timerId = 0;
26
17
  }, timeout);
@@ -541,9 +541,9 @@ class RemotePlayer extends EventTarget {
541
541
  throw new RemotePlayerError(6500, "Cannot call load() if remote player is not initialized");
542
542
  }
543
543
  if (url && window.cefQuery) {
544
- const state = await lifecycle.getState();
545
- if (state === "background" || state === "inTransitionToBackground") {
546
- throw new RemotePlayerError(6002, "Cannot call load() while in state 'background' or 'inTransitionToBackground'");
544
+ const state = lifecycle.state;
545
+ if (state === lifecycle.UiState.BACKGROUND || state === lifecycle.UiState.IN_TRANSITION_TO_BACKGROUND) {
546
+ throw new RemotePlayerError(6002, `Cannot call load() while in state '${lifecycle.UiState.BACKGROUND}' or '${lifecycle.UiState.IN_TRANSITION_TO_BACKGROUND}'`);
547
547
  }
548
548
  if (this._loadMode === this.LoadMode.LOADING) {
549
549
  throw new RemotePlayerError(6501, "Cannot call load() while previous load is still in progress");
@@ -0,0 +1,152 @@
1
+ import { remotePlayer, lifecycle } from "./api";
2
+ import { sdkLogger } from "./utils";
3
+ import shaka from "shaka-player";
4
+ /**
5
+ * SenzaShakaPlayer subclass of Shaka that handles both local and remote playback.
6
+ *
7
+ * @class SenzaShakaPlayer
8
+ *
9
+ * @example
10
+ * import { SenzaShakaPlayer } from "./senzaShakaPlayer.js";
11
+ *
12
+ * try {
13
+ * const videoElement = document.getElementById("video");
14
+ * const player = new SenzaShakaPlayer(videoElement);
15
+ * await player.load("http://playable.url/file.mpd");
16
+ * await videoElement.play(); // will start the playback
17
+ *
18
+ * } catch (err) {
19
+ * console.error("SenzaShakaPlayer failed with error", err);
20
+ * }
21
+ */
22
+ export class SenzaShakaPlayer extends shaka.Player {
23
+ /**
24
+ * Creates an instance of SenzaShakaPlayer, which is a subclass of shaka.Player.
25
+ *
26
+ * @param {HTMLVideoElement} videoElement - The video element to be used for local playback.
27
+ */
28
+ constructor(videoElement, videoContainer, dependencyInjector) {
29
+ super(videoElement, videoContainer, dependencyInjector);
30
+ this.videoElement = videoElement;
31
+ this.remotePlayer = remotePlayer;
32
+
33
+ this.remotePlayer.attach(this.videoElement);
34
+
35
+ this.addEventListeners();
36
+ }
37
+
38
+ addEventListeners() {
39
+ this.remotePlayer.addEventListener("ended", () => {
40
+ sdkLogger.log("remotePlayer ended");
41
+ lifecycle.moveToForeground();
42
+ this.videoElement.dispatchEvent(new Event("ended"));
43
+ });
44
+
45
+ this.remotePlayer.addEventListener("error", (event) => {
46
+ sdkLogger.log("remotePlayer error:", event.detail.errorCode, event.detail.message);
47
+ this.videoElement.dispatchEvent(new CustomEvent("error", event));
48
+ });
49
+
50
+ this.remotePlayer.addEventListener("timeupdate", () => {
51
+ if (!this.isInRemotePlayback) {
52
+ return;
53
+ }
54
+ this.videoElement.currentTime = this.remotePlayer.currentTime;
55
+ });
56
+
57
+ this.addEventListener("error", (event) => {
58
+ sdkLogger.log("localPlayer error:", event.detail.errorCode, event.detail.message);
59
+ });
60
+
61
+ this.videoElement.addEventListener("play", () => {
62
+ this.remotePlayer.play();
63
+ });
64
+
65
+ this.videoElement.addEventListener("pause", () => {
66
+ this.remotePlayer.pause();
67
+ });
68
+
69
+ this.videoElement.addEventListener("seeked", () => {
70
+ this.remotePlayer.currentTime = this.videoElement.currentTime;
71
+ });
72
+
73
+
74
+ this.remotePlayer.addEventListener("license-request", async (event) => {
75
+ sdkLogger.log("remotePlayer", "license-request", "Got license-request event from remote player");
76
+
77
+ // Extract license body from event
78
+ const requestBuffer = event?.detail?.licenseRequest;
79
+ const requestBufferStr = String.fromCharCode.apply(null, new Uint8Array(requestBuffer));
80
+ const decodedLicenseRequest = window.atob(requestBufferStr); // Decode from base64
81
+ const licenseRequestBytes = Uint8Array.from(decodedLicenseRequest, (l) => l.charCodeAt(0));
82
+
83
+ const request = {
84
+ body: licenseRequestBytes.buffer,
85
+ uris: [this.getConfiguration().drm.servers["com.widevine.alpha"]], // TODO: safe gaurd against undefined and other server types
86
+ method: "POST"
87
+ };
88
+
89
+ const response = await this.getNetworkingEngine().request(shaka.net.NetworkingEngine.RequestType.LICENSE, request).promise;
90
+
91
+ let responseBody = response.data;
92
+ if (response.status !== 200) {
93
+ responseBody = response.data ?? String.fromCharCode(new Uint8Array(response.data));
94
+ sdkLogger.error("remotePlayer", "license-request", "failed to to get response from widevine:", response.status, responseBody);
95
+ }
96
+ // Write response to remote player
97
+ sdkLogger.log("remotePlayer", "license-request", "Writing response to remote player", response.status);
98
+ event.writeLicenseResponse(response.status, responseBody);
99
+
100
+ });
101
+ }
102
+
103
+ /**
104
+ * Helper function that makes it easier to check if lifecycle.state is
105
+ * either background or inTransitionToBackground.
106
+ */
107
+ get isInRemotePlayback() {
108
+ return lifecycle.state === lifecycle.UiState.BACKGROUND || lifecycle.state === lifecycle.UiState.IN_TRANSITION_TO_BACKGROUND;
109
+ }
110
+
111
+ /**
112
+ * Moves playback to local player.
113
+ *
114
+ * @returns {Promise<void>}
115
+ */
116
+ async moveToLocalPlayback() {
117
+ await lifecycle.moveToForeground();
118
+ }
119
+
120
+ /**
121
+ * Moves playback to remote player.
122
+ * The timecode needs to be synced here if audiosync is not enabled.
123
+ *
124
+ * @returns {Promise<void>}
125
+ */
126
+ async moveToRemotePlayback() {
127
+ this.remotePlayer.currentTime = this.videoElement.currentTime;
128
+ await lifecycle.moveToBackground();
129
+ }
130
+
131
+ /**
132
+ * Loads a media URL into both local and remote players.
133
+ *
134
+ * @param {string} url - The URL of the media to load.
135
+ * @returns {Promise<void>}
136
+ */
137
+ async load(url, startTime, mimeType) {
138
+ if (!this.isInRemotePlayback || remotePlayer.getAssetUri() !== url) {
139
+ try {
140
+ await this.remotePlayer.load(url);
141
+ } catch (error) {
142
+ sdkLogger.log("Couldn't load remote player. Error:", error);
143
+ }
144
+ }
145
+ try {
146
+ await super.load(url, startTime, mimeType);
147
+ } catch (error) {
148
+ sdkLogger.log("Couldn't load local player. Error:", error);
149
+ }
150
+ }
151
+
152
+ }