@zcomponent/three-video-player 1.0.0-beta.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 ADDED
@@ -0,0 +1,148 @@
1
+ # @zcomponent/three-video-player
2
+
3
+ This package adds support for video playback in 3D environments to the Mattercraft 3D content creation platform for the web. It allows you to easily integrate and control video content within your 3D scenes, supporting various video formats and transparency options.
4
+
5
+ ## Features
6
+
7
+ - Supports both HLS (.m3u8) and MP4 video formats
8
+ - Offers various video transparency types, including chroma key and side-by-side transparency
9
+ - Provides control over video playback properties like volume, looping, and muting
10
+ - Seamlessly integrates with the Mattercraft 3D environment
11
+
12
+ ## Getting Started
13
+
14
+ To use the VideoPlayer component in your Mattercraft project:
15
+
16
+ 1. Right-click on a Group node in your scene's Hierarchy.
17
+ 2. Navigate to the "Media" submenu.
18
+ 3. Select "VideoPlayer" to add it to your scene.
19
+
20
+ ## Video Formats
21
+
22
+ The VideoPlayer supports two primary video formats:
23
+
24
+ - HLS (.m3u8): For adaptive streaming
25
+ - MP4: For standard video playback
26
+
27
+ ## Transparency Options
28
+
29
+ The VideoPlayer offers three transparency types:
30
+
31
+ 1. None: Standard video playback without transparency
32
+ 2. Chroma Key: Removes a specific color from the video, typically used for green screen effects
33
+ 3. Side-by-Side: Uses a side-by-side video format where one side contains the alpha channel
34
+
35
+ ## Key Properties
36
+
37
+ - `source`: The URL or file path of the video (supports .m3u8 and .mp4 files)
38
+ - `transparent`: The type of transparency to apply (none, chromaKey, or sideBySide)
39
+ - `autoplay`: Whether the video should start playing automatically
40
+ - `volume`: The playback volume (0 to 1)
41
+ - `muted`: Whether the video is muted
42
+ - `loop`: Whether the video should loop when it reaches the end
43
+
44
+ ## Chroma Key Properties
45
+
46
+ When using chroma key transparency:
47
+
48
+ - `similarity`: How closely a color must match the key color to be made transparent
49
+ - `smoothness`: The smoothness of the chroma key edges
50
+ - `spill`: Controls color spill reduction
51
+ - `keyColor`: The color to be made transparent (default is green: [0, 1, 0])
52
+
53
+ ## Side-by-Side Properties
54
+
55
+ When using side-by-side transparency:
56
+
57
+ - `direction`: The direction of the side-by-side split (LeftRight or TopBottom)
58
+ - `alphaFirst`: Whether the alpha channel is on the left/top (true) or right/bottom (false)
59
+
60
+ ## Playback Control
61
+
62
+ The VideoPlayer provides several methods for controlling playback:
63
+
64
+ - `play()`: Starts or resumes playback
65
+ - `pause()`: Pauses playback
66
+ - `stop()`: Stops playback and resets to the beginning
67
+ - `seek(time)`: Jumps to a specific time in the video (in milliseconds)
68
+
69
+ ## Events
70
+
71
+ The VideoPlayer emits several events that you can listen to:
72
+
73
+ - `onEnded`: Fired when the video playback ends
74
+ - `onPause`: Fired when the video is paused
75
+ - `onPlay`: Fired when video playback starts
76
+ - `onPlaying`: Fired when the video starts playing after being paused or stopped for buffering
77
+ - `onWaiting`: Fired when the video stops because it needs to buffer the next frame
78
+ - `onError`: Fired when an error occurs during video loading or playback
79
+
80
+ ## Example Usage
81
+
82
+ Here's a simple example of how to use the VideoPlayer in a custom behavior:
83
+
84
+ ```ts
85
+ import { Behavior, ContextManager } from '@zcomponent/core';
86
+ import { VideoPlayer } from '@zcomponent/three-video-player';
87
+ /**
88
+ * @zbehavior
89
+ */
90
+ export class MyVideoBehavior extends Behavior<VideoPlayer> {
91
+ constructor(contextManager: ContextManager, instance: VideoPlayer) {
92
+ super(contextManager, instance);
93
+
94
+ // Play the video when it's loaded
95
+ this.instance.load().then(() => {
96
+ this.instance.play();
97
+ });
98
+
99
+ // Listen for the video to end
100
+ this.register(this.instance.onEnded, () => {
101
+ console.log('Video playback ended');
102
+ });
103
+ }
104
+ }
105
+ ```
106
+
107
+ Here's an example of dynamically creating a VideoPlayer component in a custom Three.js component:
108
+
109
+ ```ts
110
+ import { Component, ContextManager, registerLoadable } from '@zcomponent/core';
111
+ import { Group } from '@zcomponent/three/lib/components/Group';
112
+ import { VideoPlayer, VideoTransparencyType } from '@zcomponent/three-video-player';
113
+
114
+ /**
115
+ * @zcomponent
116
+ */
117
+ export class CustomVideoComponent extends Group {
118
+ private videoPlayer: VideoPlayer;
119
+
120
+ constructor(contextManager: ContextManager, props: {}) {
121
+ super(contextManager, {});
122
+
123
+ // Create a new VideoPlayer instance
124
+ const source = new URL('./path/to/your/video.mp4', import.meta.url).href;
125
+
126
+ this.videoPlayer = new VideoPlayer(contextManager, {
127
+ source,
128
+ transparent: VideoTransparencyType.none,
129
+ autoplay: false
130
+ });
131
+
132
+ // Add the VideoPlayer's element to our Group
133
+ this.appendChild.add(this.videoPlayer);
134
+
135
+ }
136
+
137
+ // Example method to start playback
138
+ playVideo() {
139
+ this.videoPlayer.play();
140
+ }
141
+
142
+ // Don't forget to clean up when the component is disposed
143
+ dispose() {
144
+ this.videoPlayer.dispose();
145
+ return super.dispose();
146
+ }
147
+ }
148
+ ```
package/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './lib';
package/index.js ADDED
@@ -0,0 +1,4 @@
1
+ /* eslint-disable n/no-unsupported-features/es-syntax */
2
+ /* eslint-disable n/no-missing-import */
3
+
4
+ export * from './lib';
@@ -0,0 +1,152 @@
1
+ import * as THREE from 'three';
2
+ import { ContextManager, Event, Observable, PlayOptions, Stream } from '@zcomponent/core';
3
+ import { Object3D } from '@zcomponent/three/lib/components/Object3D';
4
+ import { MeshChromaKeyMaterial } from '@zcomponent/three/lib/internal/MeshChromaKeyMaterial';
5
+ import { MeshSideBySideAlphaMaterial, SideBySideDirection } from '@zcomponent/three/lib/internal/MeshSideBySideAlphaMaterial';
6
+ export declare enum VideoTransparencyType {
7
+ none = "none",
8
+ chromaKey = "chromaKey",
9
+ sideBySide = "sideBySide"
10
+ }
11
+ export interface BaseVideoConstructorProps {
12
+ geometry?: THREE.BufferGeometry;
13
+ /**
14
+ * Support videos with transparency
15
+ *
16
+ * @zprop
17
+ * @zdefault none
18
+ */
19
+ transparent: VideoTransparencyType;
20
+ /** @zprop
21
+ * @zdefault false
22
+ */
23
+ autoplay: boolean;
24
+ /**
25
+ * @zprop
26
+ * @zdefault default
27
+ */
28
+ audioLayer?: string;
29
+ }
30
+ /**
31
+ * Abstract base class for video components in a 3D environment.
32
+ * Supports various video transparency types, including chroma key and side-by-side transparency.
33
+ *
34
+ * Root element: [THREE.Group](https://threejs.org/docs/index.html#api/en/objects/Group)
35
+ * @zicon video_library
36
+ * @ztag three/Object3D/BaseVideo
37
+ * @zparents three/Object3D/Group/**
38
+ * @zgroup Media
39
+ * @zstream
40
+ */
41
+ export declare abstract class BaseVideo extends Object3D implements Stream {
42
+ protected constructorProps: BaseVideoConstructorProps;
43
+ onEnded: Event<[BaseVideo]>;
44
+ onPause: Event<[BaseVideo]>;
45
+ onPlay: Event<[BaseVideo]>;
46
+ onPlaying: Event<[BaseVideo]>;
47
+ onWaiting: Event<[BaseVideo]>;
48
+ onError: Event<[BaseVideo]>;
49
+ element: THREE.Group;
50
+ geometry: THREE.BufferGeometry;
51
+ material: THREE.MeshBasicMaterial | MeshChromaKeyMaterial | MeshSideBySideAlphaMaterial;
52
+ mesh: THREE.Mesh;
53
+ texture: THREE.VideoTexture;
54
+ transparent: VideoTransparencyType;
55
+ videoElement: HTMLVideoElement;
56
+ protected _gain: GainNode;
57
+ protected _hadUserEvent: boolean;
58
+ constructor(ctx: ContextManager, constructorProps: BaseVideoConstructorProps);
59
+ private _updateMute;
60
+ /**
61
+ * The volume of the video, from 0 to 1.
62
+ * @zprop
63
+ * @ztype proportion
64
+ * @zdefault 1
65
+ */
66
+ volume: Observable<number, never>;
67
+ /**
68
+ * Whether the video is muted.
69
+ * @zprop
70
+ * @zdefault false
71
+ */
72
+ muted: Observable<boolean, never>;
73
+ /**
74
+ * Whether the video should loop when it reaches the end.
75
+ * @zprop
76
+ * @zdefault false
77
+ */
78
+ loop: Observable<boolean, never>;
79
+ /**
80
+ * Plays the video.
81
+ * @zprop
82
+ * @zui
83
+ */
84
+ play(opts?: PlayOptions): void;
85
+ /**
86
+ * Pauses the video.
87
+ * @zprop
88
+ * @zui
89
+ */
90
+ pause(): void;
91
+ /**
92
+ * Stops the video and resets the playback position to the beginning.
93
+ * @zprop
94
+ * @zui
95
+ */
96
+ stop(): void;
97
+ /**
98
+ * Seeks to a specified time in the video in milliseconds.
99
+ * @zprop
100
+ * @zui
101
+ */
102
+ seek(/** @ztype time-milliseconds */ t: number): void;
103
+ length(): number | undefined;
104
+ isStalled(): boolean;
105
+ /**
106
+ * The chroma key similarity value, from 0 to 1.
107
+ * @zprop
108
+ * @zgroup Chroma Key Transparency
109
+ * @ztype proportion
110
+ * @zdefault 0.05
111
+ */
112
+ similarity: Observable<number, never>;
113
+ /**
114
+ * The smoothness of the chroma key, from 0 to 1.
115
+ * @zprop
116
+ * @zgroup Chroma Key Transparency
117
+ * @ztype proportion
118
+ * @zdefault 0.08
119
+ */
120
+ smoothness: Observable<number, never>;
121
+ /**
122
+ * The spill of the chroma key, from 0 to 1.
123
+ * @zprop
124
+ * @zgroup Chroma Key Transparency
125
+ * @ztype proportion
126
+ * @zdefault 0.2
127
+ */
128
+ spill: Observable<number, never>;
129
+ /**
130
+ * The key color of the chroma key, as an RGB array.
131
+ * @zprop
132
+ * @zgroup Chroma Key Transparency
133
+ * @ztype color-norm-rgb
134
+ * @zdefault [0, 1, 0]
135
+ */
136
+ keyColor: Observable<[number, number, number], never>;
137
+ /**
138
+ * The direction of the side-by-side transparency.
139
+ * @zprop
140
+ * @zgroup Side By Side Transparency
141
+ * @zdefault LeftRight
142
+ */
143
+ direction: Observable<SideBySideDirection, never>;
144
+ /**
145
+ * Whether the alpha channel is first or second in the side-by-side transparency.
146
+ * @zprop
147
+ * @zgroup Side By Side Transparency
148
+ * @zdefault false
149
+ */
150
+ alphaFirst: Observable<boolean, never>;
151
+ dispose(): never;
152
+ }
@@ -0,0 +1,230 @@
1
+ import * as THREE from 'three';
2
+ import { AudioContextContext, Event, isDesignTime, nextUserEvent, Observable, registerLoadable, started, useOnBeforeRender } from '@zcomponent/core';
3
+ import { Object3D } from '@zcomponent/three/lib/components/Object3D';
4
+ import { MeshChromaKeyMaterial } from '@zcomponent/three/lib/internal/MeshChromaKeyMaterial';
5
+ import { MeshSideBySideAlphaMaterial, SideBySideDirection } from '@zcomponent/three/lib/internal/MeshSideBySideAlphaMaterial';
6
+ import { EncodingType } from '@zcomponent/three/lib';
7
+ import { legacy_set_texture_encoding } from '@zcomponent/three/lib/utils';
8
+ export var VideoTransparencyType;
9
+ (function (VideoTransparencyType) {
10
+ VideoTransparencyType["none"] = "none";
11
+ VideoTransparencyType["chromaKey"] = "chromaKey";
12
+ VideoTransparencyType["sideBySide"] = "sideBySide";
13
+ })(VideoTransparencyType || (VideoTransparencyType = {}));
14
+ /**
15
+ * Abstract base class for video components in a 3D environment.
16
+ * Supports various video transparency types, including chroma key and side-by-side transparency.
17
+ *
18
+ * Root element: [THREE.Group](https://threejs.org/docs/index.html#api/en/objects/Group)
19
+ * @zicon video_library
20
+ * @ztag three/Object3D/BaseVideo
21
+ * @zparents three/Object3D/Group/**
22
+ * @zgroup Media
23
+ * @zstream
24
+ */
25
+ export class BaseVideo extends Object3D {
26
+ constructor(ctx, constructorProps) {
27
+ super(ctx, constructorProps, new THREE.Group());
28
+ this.constructorProps = constructorProps;
29
+ this.onEnded = new Event();
30
+ this.onPause = new Event();
31
+ this.onPlay = new Event();
32
+ this.onPlaying = new Event();
33
+ this.onWaiting = new Event();
34
+ this.onError = new Event();
35
+ this._hadUserEvent = false;
36
+ this._updateMute = () => {
37
+ if (!this.videoElement || !this._hadUserEvent)
38
+ return;
39
+ this.videoElement.muted = this.muted.value;
40
+ };
41
+ /**
42
+ * The volume of the video, from 0 to 1.
43
+ * @zprop
44
+ * @ztype proportion
45
+ * @zdefault 1
46
+ */
47
+ this.volume = new Observable(1, v => (this._gain.gain.value = v));
48
+ /**
49
+ * Whether the video is muted.
50
+ * @zprop
51
+ * @zdefault false
52
+ */
53
+ this.muted = new Observable(false);
54
+ /**
55
+ * Whether the video should loop when it reaches the end.
56
+ * @zprop
57
+ * @zdefault false
58
+ */
59
+ this.loop = new Observable(false, v => {
60
+ this.videoElement.loop = v;
61
+ });
62
+ /**
63
+ * The chroma key similarity value, from 0 to 1.
64
+ * @zprop
65
+ * @zgroup Chroma Key Transparency
66
+ * @ztype proportion
67
+ * @zdefault 0.05
68
+ */
69
+ this.similarity = new Observable(0.05);
70
+ /**
71
+ * The smoothness of the chroma key, from 0 to 1.
72
+ * @zprop
73
+ * @zgroup Chroma Key Transparency
74
+ * @ztype proportion
75
+ * @zdefault 0.08
76
+ */
77
+ this.smoothness = new Observable(0.08);
78
+ /**
79
+ * The spill of the chroma key, from 0 to 1.
80
+ * @zprop
81
+ * @zgroup Chroma Key Transparency
82
+ * @ztype proportion
83
+ * @zdefault 0.2
84
+ */
85
+ this.spill = new Observable(0.2);
86
+ /**
87
+ * The key color of the chroma key, as an RGB array.
88
+ * @zprop
89
+ * @zgroup Chroma Key Transparency
90
+ * @ztype color-norm-rgb
91
+ * @zdefault [0, 1, 0]
92
+ */
93
+ this.keyColor = new Observable([0, 1, 0]);
94
+ /**
95
+ * The direction of the side-by-side transparency.
96
+ * @zprop
97
+ * @zgroup Side By Side Transparency
98
+ * @zdefault LeftRight
99
+ */
100
+ this.direction = new Observable(SideBySideDirection.LeftRight);
101
+ /**
102
+ * Whether the alpha channel is first or second in the side-by-side transparency.
103
+ * @zprop
104
+ * @zgroup Side By Side Transparency
105
+ * @zdefault false
106
+ */
107
+ this.alphaFirst = new Observable(false);
108
+ const videoElement = document.createElement('video');
109
+ videoElement.crossOrigin = 'anonymous';
110
+ videoElement.playsInline = true;
111
+ videoElement.muted = true;
112
+ videoElement.preload = 'auto';
113
+ videoElement.onended = () => this.onEnded.emit(this);
114
+ videoElement.onpause = () => this.onPause.emit(this);
115
+ videoElement.onplay = () => this.onPlay.emit(this);
116
+ videoElement.onplaying = () => this.onPlaying.emit(this);
117
+ videoElement.onwaiting = () => this.onWaiting.emit(this);
118
+ videoElement.onerror = () => this.onError.emit(this);
119
+ this.geometry = constructorProps.geometry ?? new THREE.PlaneGeometry();
120
+ registerLoadable(ctx, new Promise((resolve, reject) => {
121
+ videoElement.addEventListener('canplaythrough', () => {
122
+ resolve();
123
+ });
124
+ videoElement.addEventListener('error', err => {
125
+ console.error(err);
126
+ reject();
127
+ });
128
+ }));
129
+ const audioContextContext = ctx.get(AudioContextContext);
130
+ this._gain = new GainNode(audioContextContext.audioContext);
131
+ const audioSource = audioContextContext.audioContext.createMediaElementSource(videoElement);
132
+ audioSource.connect(this._gain);
133
+ this._gain.connect(audioContextContext.getLayer(constructorProps.audioLayer));
134
+ this.transparent = constructorProps.transparent ?? VideoTransparencyType.none;
135
+ this.videoElement = videoElement;
136
+ this.texture = new THREE.VideoTexture(videoElement);
137
+ legacy_set_texture_encoding(this.texture, EncodingType.sRGBEncoding);
138
+ if (this.transparent === VideoTransparencyType.chromaKey) {
139
+ const mat = new MeshChromaKeyMaterial();
140
+ this.material = mat;
141
+ mat.map = this.texture;
142
+ this.register(this.similarity, v => (mat.similarity = v));
143
+ this.register(this.smoothness, v => (mat.smoothness = v));
144
+ this.register(this.spill, v => (mat.spill = v));
145
+ this.register(this.keyColor, v => (mat.keyColor = [...v]));
146
+ this.register(useOnBeforeRender(this.contextManager), () => mat.frame());
147
+ }
148
+ else if (this.transparent === VideoTransparencyType.sideBySide) {
149
+ const mat = new MeshSideBySideAlphaMaterial();
150
+ this.material = mat;
151
+ mat.map = this.texture;
152
+ this.register(this.alphaFirst, v => (mat.alphaFirst = v));
153
+ this.register(this.direction, v => (mat.direction = v));
154
+ this.register(useOnBeforeRender(this.contextManager), () => mat.frame());
155
+ }
156
+ else {
157
+ this.material = new THREE.MeshBasicMaterial({ toneMapped: false, map: this.texture });
158
+ }
159
+ this.mesh = new THREE.Mesh(this.geometry, this.material);
160
+ this.element.add(this.mesh);
161
+ this.videoElement.addEventListener('loadedmetadata', () => {
162
+ let transparencyAspectFix = 1;
163
+ if (this.transparent === VideoTransparencyType.sideBySide) {
164
+ transparencyAspectFix = this.direction.value === SideBySideDirection.LeftRight ? 0.5 : 2;
165
+ }
166
+ this.mesh.scale.set((transparencyAspectFix * this.videoElement.videoWidth) / this.videoElement.videoHeight, 1, 1);
167
+ });
168
+ this.register(this.muted, this._updateMute);
169
+ nextUserEvent(ctx).then(() => {
170
+ this._hadUserEvent = true;
171
+ this._updateMute();
172
+ });
173
+ if (!isDesignTime(ctx) && constructorProps.autoplay)
174
+ started(ctx).then(() => this.play());
175
+ }
176
+ /**
177
+ * Plays the video.
178
+ * @zprop
179
+ * @zui
180
+ */
181
+ play(opts) {
182
+ this.videoElement.playbackRate = opts?.speed ?? 1;
183
+ this.videoElement.play();
184
+ }
185
+ /**
186
+ * Pauses the video.
187
+ * @zprop
188
+ * @zui
189
+ */
190
+ pause() {
191
+ this.videoElement.pause();
192
+ }
193
+ /**
194
+ * Stops the video and resets the playback position to the beginning.
195
+ * @zprop
196
+ * @zui
197
+ */
198
+ stop() {
199
+ this.videoElement.pause();
200
+ this.videoElement.currentTime = 0;
201
+ }
202
+ /**
203
+ * Seeks to a specified time in the video in milliseconds.
204
+ * @zprop
205
+ * @zui
206
+ */
207
+ seek(/** @ztype time-milliseconds */ t) {
208
+ this.videoElement.currentTime = t / 1000;
209
+ }
210
+ length() {
211
+ return isNaN(this.videoElement.duration) ? undefined : this.videoElement.duration * 1000;
212
+ }
213
+ isStalled() {
214
+ return this.videoElement.readyState <= 2;
215
+ }
216
+ dispose() {
217
+ this.geometry.dispose();
218
+ this.material.map?.dispose();
219
+ this.material.dispose();
220
+ this.texture.dispose();
221
+ this.videoElement.pause();
222
+ this.videoElement.onended = null;
223
+ this.videoElement.onpause = null;
224
+ this.videoElement.onplay = null;
225
+ this.videoElement.onplaying = null;
226
+ this.videoElement.onwaiting = null;
227
+ this.videoElement.onerror = null;
228
+ return super.dispose();
229
+ }
230
+ }
@@ -0,0 +1,33 @@
1
+ import { ContextManager } from '@zcomponent/core';
2
+ import { BaseVideo, BaseVideoConstructorProps } from './basevideo';
3
+ export interface VideoConstructorProps extends BaseVideoConstructorProps {
4
+ /** @zprop
5
+ * @zvalues files *.m3u8, *.mp4
6
+ */
7
+ source: string;
8
+ }
9
+ /**
10
+ *
11
+ * Manages the playback and display of a video in a 3D environment.
12
+ * Supports various video transparency types, including chroma key and side-by-side transparency.
13
+ * Provides control over video playback properties like volume, looping, and muting.
14
+ *
15
+ * Supports both HLS and MP4 video formats.
16
+ *
17
+ *
18
+ * Root element: [THREE.Group](https://threejs.org/docs/index.html#api/en/objects/Group)
19
+ * @zcomponent
20
+ * @zicon video_library
21
+ * @ztag three/Object3D/Video
22
+ * @zparents three/Object3D/Group/**
23
+ * @zgroup Media
24
+ */
25
+ export declare class VideoPlayer extends BaseVideo {
26
+ protected constructorProps: VideoConstructorProps;
27
+ private hls;
28
+ constructor(ctx: ContextManager, constructorProps: VideoConstructorProps);
29
+ load(): Promise<void>;
30
+ private _loadHlsVideo;
31
+ private _loadMp4Video;
32
+ dispose(): never;
33
+ }
@@ -0,0 +1,94 @@
1
+ // TODO- import type and dynamic import later
2
+ import Hls from 'hls.js';
3
+ import { BaseVideo } from './basevideo';
4
+ /**
5
+ *
6
+ * Manages the playback and display of a video in a 3D environment.
7
+ * Supports various video transparency types, including chroma key and side-by-side transparency.
8
+ * Provides control over video playback properties like volume, looping, and muting.
9
+ *
10
+ * Supports both HLS and MP4 video formats.
11
+ *
12
+ *
13
+ * Root element: [THREE.Group](https://threejs.org/docs/index.html#api/en/objects/Group)
14
+ * @zcomponent
15
+ * @zicon video_library
16
+ * @ztag three/Object3D/Video
17
+ * @zparents three/Object3D/Group/**
18
+ * @zgroup Media
19
+ */
20
+ export class VideoPlayer extends BaseVideo {
21
+ constructor(ctx, constructorProps) {
22
+ super(ctx, constructorProps);
23
+ this.constructorProps = constructorProps;
24
+ this.hls = null;
25
+ }
26
+ async load() {
27
+ const isHls = this.constructorProps.source.endsWith('.m3u8');
28
+ return isHls ? this._loadHlsVideo() : this._loadMp4Video();
29
+ }
30
+ async _loadHlsVideo() {
31
+ if (!Hls.isSupported()) {
32
+ // Consider adding a fallback to MP4
33
+ console.error('HLS is not supported in this browser.');
34
+ return;
35
+ }
36
+ this.hls = new Hls();
37
+ return new Promise((resolve, reject) => {
38
+ this.hls?.on(Hls.Events.MEDIA_ATTACHED, () => { });
39
+ this.hls?.on(Hls.Events.MANIFEST_PARSED, () => {
40
+ resolve();
41
+ });
42
+ this.hls?.on(Hls.Events.ERROR, (event, data) => {
43
+ if (data.fatal) {
44
+ switch (data.type) {
45
+ case Hls.ErrorTypes.NETWORK_ERROR:
46
+ console.error('HLS: Fatal network error encountered, trying to recover');
47
+ this.hls?.startLoad();
48
+ break;
49
+ case Hls.ErrorTypes.MEDIA_ERROR:
50
+ console.error('HLS: Fatal media error encountered, trying to recover');
51
+ this.hls?.recoverMediaError();
52
+ break;
53
+ default:
54
+ console.error('HLS: Fatal error, cannot recover');
55
+ this.hls?.destroy();
56
+ reject('HLS: Fatal error, cannot recover');
57
+ break;
58
+ }
59
+ }
60
+ });
61
+ this.hls?.loadSource(this.constructorProps.source);
62
+ this.hls?.attachMedia(this.videoElement);
63
+ });
64
+ }
65
+ async _loadMp4Video() {
66
+ return new Promise((resolve, reject) => {
67
+ const onCanPlay = () => {
68
+ cleanup();
69
+ resolve();
70
+ };
71
+ const onError = e => {
72
+ console.error('MP4: Error loading video', e);
73
+ cleanup();
74
+ reject(new Error('MP4 video loading error'));
75
+ };
76
+ const cleanup = () => {
77
+ this.videoElement.removeEventListener('canplay', onCanPlay);
78
+ this.videoElement.removeEventListener('error', onError);
79
+ };
80
+ this.videoElement.addEventListener('canplay', onCanPlay);
81
+ this.videoElement.addEventListener('error', onError);
82
+ this.videoElement.src = this.constructorProps.source;
83
+ this.videoElement.load();
84
+ });
85
+ }
86
+ dispose() {
87
+ if (this.hls) {
88
+ this.hls.destroy();
89
+ this.hls = null;
90
+ }
91
+ this.videoElement.src = '';
92
+ return super.dispose();
93
+ }
94
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { VideoTransparencyType } from './components/basevideo';
2
+ export { VideoConstructorProps } from './components/videoplayer';
3
+ export { VideoPlayer } from './components/videoplayer';
package/lib/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { VideoTransparencyType } from './components/basevideo';
2
+ export { VideoPlayer } from './components/videoplayer';
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@zcomponent/three-video-player",
3
+ "version": "1.0.0-beta.1",
4
+ "description": "HLS & MP4 Player for Mattercraft",
5
+ "author": "Zappar Limited",
6
+ "license": "Proprietary",
7
+ "main": "index.js",
8
+ "types": "index.d.ts",
9
+ "zexports": [
10
+ "./lib/**/*"
11
+ ],
12
+ "directories": {
13
+ "lib": "lib"
14
+ },
15
+ "files": [
16
+ "index.js",
17
+ "index.d.ts",
18
+ "lib/**/*",
19
+ "public/**/*"
20
+ ],
21
+ "scripts": {
22
+ "build": "rm -rf lib && tsc",
23
+ "watch": "tsc -w",
24
+ "prepack": "npm run build",
25
+ "docs": "typedoc --out docs"
26
+ },
27
+ "dependencies": {
28
+ "hls.js": "^1.5.12"
29
+ },
30
+ "devDependencies": {
31
+ "@zcomponent/three": "1.26.1-beta",
32
+ "three": "^0.150.1"
33
+ },
34
+ "peerDependencies": {
35
+ "@zcomponent/three": "1.x",
36
+ "@zcomponent/core": "1.x",
37
+ "three": "0.150.1 - 164.0"
38
+ },
39
+ "keywords": [],
40
+ "release": {
41
+ "extends": "semantic-release-commit-filter",
42
+ "ci": true,
43
+ "branches": [
44
+ "main",
45
+ {
46
+ "name": "beta",
47
+ "channel": "beta",
48
+ "prerelease": "beta"
49
+ },
50
+ {
51
+ "name": "alpha",
52
+ "channel": "alpha",
53
+ "prerelease": "alpha"
54
+ }
55
+ ],
56
+ "plugins": [
57
+ "@semantic-release/commit-analyzer",
58
+ [
59
+ "@semantic-release/release-notes-generator",
60
+ {
61
+ "writerOpts": {
62
+ "commitUrlFormat": null,
63
+ "issueUrlFormat": null,
64
+ "commitPartial": "* {{header}}{{#if subject}} {{subject}}{{/if}}"
65
+ }
66
+ }
67
+ ],
68
+ "@semantic-release/changelog",
69
+ "@semantic-release/npm",
70
+ "@semantic-release/gitlab",
71
+ "@semantic-release/git"
72
+ ],
73
+ "tagFormat": "@zcomponent/three-video-player@${version}"
74
+ }
75
+ }