@clockworkdog/cogs-client 1.4.3 → 1.5.1

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/README.md CHANGED
@@ -6,21 +6,33 @@ Create content for your COGS Media Master
6
6
 
7
7
  ## Add to your project
8
8
 
9
- ### Browser
9
+ ### Static HTML
10
+
11
+ Download `cogs-client.js` from https://unpkg.com/@clockworkdog/cogs-client and save it to your project.
12
+
13
+ Include the script in your HTML page:
10
14
 
11
15
  ```html
12
- <script src="https://unpkg.com/@clockworkdog/cogs-client@1"></script>
16
+ <script src="./cogs-client.js"></script>
13
17
  ```
14
18
 
15
- ### NPM
19
+ (Avoid `<script>` tags with `http...` so your content works without an internet connection.)
20
+
21
+ ### NPM / Yarn
22
+
23
+ If you haven't yet created a web project we recommend using Vite with Typescript:
16
24
 
17
25
  ```shell
18
- npm install --save @clockworkdog/cogs-client
26
+ yarn create vite my-custom-content --template vanilla-ts
27
+ cd my-custom-content
28
+ yarn
19
29
  ```
20
30
 
21
- ### Yarn
31
+ Then add `cogs-client` with NPM or Yarn:
22
32
 
23
33
  ```shell
34
+ npm install --save @clockworkdog/cogs-client
35
+ # OR
24
36
  yarn add @clockworkdog/cogs-client
25
37
  ```
26
38
 
@@ -405,12 +405,14 @@ function isFadeValid(fade) {
405
405
  return typeof fade === 'number' && !isNaN(fade) && fade > 0;
406
406
  }
407
407
  function setPlayerSinkId(player, sinkId) {
408
+ var _a;
408
409
  if (sinkId === undefined) {
409
410
  return;
410
411
  }
411
412
  if (player._html5) {
412
- player._sounds.forEach((sound) => {
413
- sound._node.setSinkId(sinkId);
413
+ (_a = player._sounds) === null || _a === void 0 ? void 0 : _a.forEach((sound) => {
414
+ var _a, _b;
415
+ (_b = (_a = sound._node) === null || _a === void 0 ? void 0 : _a.setSinkId) === null || _b === void 0 ? void 0 : _b.call(_a, sinkId);
414
416
  });
415
417
  }
416
418
  else {
@@ -1,4 +1,4 @@
1
- import { Html5VideoPipeline } from '@clockworkdog/media-stream-library-browser';
1
+ export declare const LIVE_VIDEO_PLAYBACK_RATE = 1.1;
2
2
  /**
3
3
  * Manages a websocket connection to the COGS TCP relay which can be used to send RTSP video
4
4
  * feeds to the web.
@@ -12,11 +12,14 @@ export default class RtspStreamer {
12
12
  });
13
13
  /**
14
14
  * Start an RTSP video stream on with the given URI on the given video element.
15
- * @returns The playing HTML5 video pipeline.
15
+ * @returns An object with a function to close the pipeline
16
16
  */
17
17
  play(params: {
18
18
  uri: string;
19
19
  videoElement: HTMLVideoElement;
20
20
  playbackRate?: number;
21
- }): Html5VideoPipeline;
21
+ restartIfStopped?: boolean;
22
+ }): {
23
+ close: () => void;
24
+ };
22
25
  }
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LIVE_VIDEO_PLAYBACK_RATE = void 0;
3
4
  const media_stream_library_browser_1 = require("@clockworkdog/media-stream-library-browser");
4
5
  const urls_1 = require("./helpers/urls");
6
+ const DEFAULT_VIDEO_PLAYBACK_RATE = 1;
5
7
  // Use a faster-than-realtime playback rate by default
6
8
  // so that it keeps up with a realtime stream in case of video buffering
7
- const DEFAULT_VIDEO_PLAYBACK_RATE = 1.1;
9
+ exports.LIVE_VIDEO_PLAYBACK_RATE = 1.1;
8
10
  /**
9
11
  * Manages a websocket connection to the COGS TCP relay which can be used to send RTSP video
10
12
  * feeds to the web.
@@ -15,32 +17,83 @@ class RtspStreamer {
15
17
  }
16
18
  /**
17
19
  * Start an RTSP video stream on with the given URI on the given video element.
18
- * @returns The playing HTML5 video pipeline.
20
+ * @returns An object with a function to close the pipeline
19
21
  */
20
22
  play(params) {
21
23
  var _a;
22
24
  const { uri, videoElement } = params;
23
- const pipeline = new media_stream_library_browser_1.Html5VideoPipeline({
24
- ws: { uri: this._websocketUri },
25
- rtsp: { uri: uri },
26
- mediaElement: videoElement,
27
- });
28
- // Restart stream on RTCP BYE (stream ended)
29
- pipeline.rtsp.onRtcp = (rtcp) => {
30
- if (media_stream_library_browser_1.isRtcpBye(rtcp)) {
31
- setTimeout(() => this.play(params), 0);
32
- }
25
+ videoElement.playsInline = true; // Required for iOS
26
+ let pipeline;
27
+ const startPipeline = () => {
28
+ pipeline === null || pipeline === void 0 ? void 0 : pipeline.close();
29
+ pipeline = new media_stream_library_browser_1.Html5VideoPipeline({
30
+ ws: { uri: this._websocketUri },
31
+ rtsp: { uri: uri },
32
+ mediaElement: videoElement,
33
+ });
34
+ // Restart stream on RTCP BYE (stream ended)
35
+ pipeline.rtsp.onRtcp = (rtcp) => {
36
+ if (media_stream_library_browser_1.isRtcpBye(rtcp)) {
37
+ console.log('Video stream ended. Restarting.');
38
+ videoElement.pause();
39
+ setTimeout(startPipeline, 0);
40
+ }
41
+ };
42
+ // Start playback when ready
43
+ pipeline.ready.then(() => {
44
+ pipeline.rtsp.play();
45
+ });
46
+ };
47
+ startPipeline();
48
+ if (params.playbackRate) {
49
+ const playbackRate = (_a = params.playbackRate) !== null && _a !== void 0 ? _a : DEFAULT_VIDEO_PLAYBACK_RATE;
50
+ videoElement.playbackRate = playbackRate;
51
+ videoElement.addEventListener('play', () => {
52
+ videoElement.playbackRate = playbackRate;
53
+ });
54
+ }
55
+ let removeRestartListeners = null;
56
+ if (params.restartIfStopped) {
57
+ let playing = false;
58
+ let interval = null;
59
+ const handleTimeUpdate = () => {
60
+ playing = true;
61
+ };
62
+ const handlePlay = () => {
63
+ playing = true;
64
+ videoElement.addEventListener('timeupdate', handleTimeUpdate);
65
+ if (!interval) {
66
+ interval = setInterval(() => {
67
+ if (!playing) {
68
+ console.log('Video stopped playing. Restarting.');
69
+ videoElement.pause();
70
+ setTimeout(startPipeline, 0);
71
+ }
72
+ playing = false;
73
+ }, 2000);
74
+ }
75
+ };
76
+ const handlePause = () => {
77
+ videoElement.removeEventListener('timeupdate', handleTimeUpdate);
78
+ if (interval) {
79
+ clearInterval(interval);
80
+ interval = null;
81
+ }
82
+ };
83
+ videoElement.addEventListener('play', handlePlay);
84
+ videoElement.addEventListener('pause', handlePause);
85
+ removeRestartListeners = () => {
86
+ handlePause();
87
+ videoElement.removeEventListener('play', handlePlay);
88
+ videoElement.removeEventListener('pause', handlePause);
89
+ };
90
+ }
91
+ return {
92
+ close: () => {
93
+ pipeline === null || pipeline === void 0 ? void 0 : pipeline.close();
94
+ removeRestartListeners === null || removeRestartListeners === void 0 ? void 0 : removeRestartListeners();
95
+ },
33
96
  };
34
- // Start playback when ready
35
- pipeline.ready.then(() => {
36
- pipeline.rtsp.play();
37
- });
38
- videoElement.playbackRate = (_a = params.playbackRate) !== null && _a !== void 0 ? _a : DEFAULT_VIDEO_PLAYBACK_RATE;
39
- videoElement.addEventListener('play', () => {
40
- var _a;
41
- videoElement.playbackRate = (_a = params.playbackRate) !== null && _a !== void 0 ? _a : DEFAULT_VIDEO_PLAYBACK_RATE;
42
- });
43
- return pipeline;
44
97
  }
45
98
  }
46
99
  exports.default = RtspStreamer;
@@ -266,6 +266,7 @@ class VideoPlayer {
266
266
  }
267
267
  createVideoElement(path, config, { volume }) {
268
268
  const videoElement = document.createElement('video');
269
+ videoElement.playsInline = true; // Required for iOS
269
270
  videoElement.src = urls_1.assetUrl(path);
270
271
  videoElement.autoplay = false;
271
272
  videoElement.loop = false;
@@ -321,5 +322,7 @@ function setPlayerSinkId(player, sinkId) {
321
322
  if (sinkId === undefined) {
322
323
  return;
323
324
  }
324
- player.videoElement.setSinkId(sinkId);
325
+ if (typeof player.videoElement.setSinkId === 'function') {
326
+ player.videoElement.setSinkId(sinkId);
327
+ }
325
328
  }