@cntrl-site/sdk-nextjs 0.25.0 → 0.26.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.
Files changed (32) hide show
  1. package/cntrl-site-sdk-nextjs-0.25.1.tgz +0 -0
  2. package/lib/components/Article.js +4 -4
  3. package/lib/components/ArticleWrapper.js +2 -2
  4. package/lib/components/Item.js +8 -7
  5. package/lib/components/LayoutStyle.js +2 -2
  6. package/lib/components/LinkWrapper.js +1 -1
  7. package/lib/components/Page.js +1 -1
  8. package/lib/components/ScrollPlaybackVideo.js +46 -0
  9. package/lib/components/Section.js +4 -4
  10. package/lib/components/items/CustomItem.js +2 -2
  11. package/lib/components/items/GroupItem.js +3 -3
  12. package/lib/components/items/ImageItem.js +3 -3
  13. package/lib/components/items/RectangleItem.js +3 -3
  14. package/lib/components/items/RichTextItem.js +3 -3
  15. package/lib/components/items/VideoItem.js +16 -44
  16. package/lib/components/items/VimeoEmbed.js +4 -4
  17. package/lib/components/items/YoutubeEmbed.js +3 -3
  18. package/lib/components/useItemPosition.js +5 -1
  19. package/lib/provider/CntrlProvider.js +1 -1
  20. package/lib/utils/PlaybackVideoConverter/ScrollPlaybackVideoManager.js +221 -0
  21. package/lib/utils/PlaybackVideoConverter/VideoDecoder.js +175 -0
  22. package/lib/utils/RichTextConverter/RichTextConverter.js +4 -4
  23. package/lib/utils/Youtube/YoutubeIframeApi.js +1 -1
  24. package/package.json +6 -3
  25. package/src/components/Item.tsx +6 -4
  26. package/src/components/ScrollPlaybackVideo.tsx +56 -0
  27. package/src/components/items/VideoItem.tsx +37 -55
  28. package/src/components/useItemPosition.ts +6 -2
  29. package/src/utils/PlaybackVideoConverter/ScrollPlaybackVideoManager.ts +211 -0
  30. package/src/utils/PlaybackVideoConverter/VideoDecoder.ts +174 -0
  31. package/.idea/codeStyles/codeStyleConfig.xml +0 -5
  32. package/.idea/inspectionProfiles/Project_Default.xml +0 -15
@@ -1,4 +1,4 @@
1
- import { FC, useContext, useEffect, useId, useMemo, useRef, useState } from 'react';
1
+ import { FC, useId, useMemo, useRef, useState } from 'react';
2
2
  import JSXStyle from 'styled-jsx/style';
3
3
  import { CntrlColor } from '@cntrl-site/color';
4
4
  import { ArticleItemType, getLayoutStyles, VideoItem as TVideoItem } from '@cntrl-site/sdk';
@@ -9,13 +9,8 @@ import { useItemAngle } from '../useItemAngle';
9
9
  import { useCntrlContext } from '../../provider/useCntrlContext';
10
10
  import { getHoverStyles, getTransitions } from '../../utils/HoverStyles/HoverStyles';
11
11
  import { useRegisterResize } from "../../common/useRegisterResize";
12
- import { rangeMap } from '../../utils/rangeMap';
13
- import { ArticleRectContext } from '../../provider/ArticleRectContext';
14
12
  import { useLayoutContext } from '../useLayoutContext';
15
-
16
- // To prevent video behaviour that drops to the first frame
17
- // when close to the end
18
- const SCROLL_TIME_SHIFT = 0.1;
13
+ import { ScrollPlaybackVideo } from '../ScrollPlaybackVideo';
19
14
 
20
15
  export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId, onResize }) => {
21
16
  const id = useId();
@@ -25,40 +20,12 @@ export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId, onResize
25
20
  const borderColor = useMemo(() => CntrlColor.parse(strokeColor), [strokeColor]);
26
21
  const [ref, setRef] = useState<HTMLDivElement | null>(null);
27
22
  const videoRef = useRef<HTMLVideoElement | null>(null);
28
- const articleRectObserver = useContext(ArticleRectContext);
29
- const rafId = useRef<number | undefined>();
30
23
  const layoutId = useLayoutContext();
31
- const scrollPlayback = item.layoutParams[layoutId!].scrollPlayback
32
-
33
- useEffect(() => {
34
- const video = videoRef.current;
35
- if (!video || !scrollPlayback) {
36
- video?.play();
37
- return;
38
- }
39
- video?.pause();
40
- const scrollVideo = () => {
41
- rafId.current = window.requestAnimationFrame(scrollVideo);
42
- if (!articleRectObserver) return;
43
- const scrollPos = articleRectObserver.getSectionScroll(sectionId);
44
- if (!video.duration) return;
45
- const time = rangeMap(scrollPos, scrollPlayback.from, scrollPlayback.to, 0, video.duration, true);
46
- if (scrollPos > scrollPlayback.from && scrollPos < scrollPlayback.to) {
47
- if (toFixed(video.currentTime) === toFixed(time - SCROLL_TIME_SHIFT)) return;
48
- video.currentTime = Math.max(0, time - SCROLL_TIME_SHIFT);
49
- }
50
- };
51
- rafId.current = window.requestAnimationFrame(scrollVideo);
52
-
53
- return () => {
54
- if (rafId.current) {
55
- window.cancelAnimationFrame(rafId.current);
56
- rafId.current = undefined;
57
- }
58
- }
59
- }, [scrollPlayback]);
24
+ const scrollPlayback = item.layoutParams[layoutId!].scrollPlayback;
25
+ const hasScrollPlayback = scrollPlayback !== null;
60
26
 
61
27
  useRegisterResize(ref, onResize);
28
+
62
29
  return (
63
30
  <LinkWrapper url={item.link?.url} target={item.link?.target}>
64
31
  <div
@@ -67,24 +34,39 @@ export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId, onResize
67
34
  style={{
68
35
  opacity: `${opacity}`,
69
36
  transform: `rotate(${angle}deg)`,
70
- filter: `blur(${blur * 100}vw)`
37
+ filter: `blur(${blur * 100}vw)`,
38
+ overflow: hasScrollPlayback ? 'hidden' : 'auto'
71
39
  }}
72
40
  >
73
- <video
74
- ref={videoRef}
75
- autoPlay
76
- muted
77
- loop
78
- playsInline
79
- className={`video video-${item.id}`}
80
- style={{
41
+ {hasScrollPlayback ? (
42
+ <ScrollPlaybackVideo
43
+ sectionId={sectionId}
44
+ src={item.commonParams.url}
45
+ playbackParams={scrollPlayback}
46
+ style={{
47
+ borderRadius: `${radius * 100}vw`,
48
+ borderWidth: `${strokeWidth * 100}vw`,
49
+ borderColor: `${borderColor.toCss()}`
50
+ }}
51
+ className={`video video-playback-wrapper video-${item.id}`}
52
+ />
53
+ ) : (
54
+ <video
55
+ ref={videoRef}
56
+ autoPlay
57
+ muted
58
+ loop
59
+ playsInline
60
+ className={`video video-${item.id}`}
61
+ style={{
81
62
  borderRadius: `${radius * 100}vw`,
82
63
  borderWidth: `${strokeWidth * 100}vw`,
83
64
  borderColor: `${borderColor.toCss()}`
84
- }}
85
- >
86
- <source src={item.commonParams.url} />
87
- </video>
65
+ }}
66
+ >
67
+ <source src={item.commonParams.url} />
68
+ </video>
69
+ )}
88
70
  </div>
89
71
  <JSXStyle id={id}>{`
90
72
  @supports not (color: oklch(42% 0.3 90 / 1)) {
@@ -111,6 +93,10 @@ export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId, onResize
111
93
  .video-${item.id} {
112
94
  border-color: ${strokeColor};
113
95
  }
96
+ .video-playback-wrapper {
97
+ display: flex;
98
+ justify-content: center;
99
+ }
114
100
  ${getLayoutStyles(layouts, [item.state.hover], ([hoverParams]) => {
115
101
  return (`
116
102
  .video-wrapper-${item.id} {
@@ -131,7 +117,3 @@ export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId, onResize
131
117
  </LinkWrapper>
132
118
  );
133
119
  };
134
-
135
- function toFixed(num: number) {
136
- return Number(num.toFixed(5));
137
- }
@@ -1,4 +1,4 @@
1
- import { AnchorSide, ItemAny } from '@cntrl-site/sdk';
1
+ import { AnchorSide, ItemAny, PositionType } from '@cntrl-site/sdk';
2
2
  import { useKeyframeValue } from '../common/useKeyframeValue';
3
3
  import { getItemTopStyle } from '../utils/getItemTopStyle';
4
4
  import { useLayoutContext } from './useLayoutContext';
@@ -16,8 +16,12 @@ export const useItemPosition = (item: ItemAny, sectionId: string) => {
16
16
  [layoutId]
17
17
  );
18
18
  const anchorSide = layoutId ? item.area[layoutId].anchorSide : AnchorSide.Top;
19
+ const positionType = layoutId ? item.area[layoutId].positionType : PositionType.ScreenBased;
20
+ // tp prevent fixed item (with anchor point bottom) to jump when scroll in safari on mobile
21
+ const isScreenBasedBottom = positionType === PositionType.ScreenBased && anchorSide === AnchorSide.Bottom;
19
22
  return {
20
- top: getItemTopStyle(top, anchorSide),
23
+ bottom: isScreenBasedBottom ? `${-top * 100}vw` : 'unset',
24
+ top: isScreenBasedBottom ? 'unset' : getItemTopStyle(top, anchorSide),
21
25
  left: `${left * 100}vw`
22
26
  };
23
27
  };
@@ -0,0 +1,211 @@
1
+ import UAParser from 'ua-parser-js';
2
+ import videoDecoder from './VideoDecoder';
3
+
4
+ interface ScrollVideoOptions {
5
+ src: string;
6
+ videoContainer: HTMLElement | string;
7
+ }
8
+
9
+ export class ScrollPlaybackVideoManager {
10
+ private container?: HTMLElement;
11
+ private video?: HTMLVideoElement;
12
+ private isSafari?: boolean;
13
+ private currentTime = 0;
14
+ private targetTime = 0;
15
+ private canvas: HTMLCanvasElement | null = null;
16
+ private context: CanvasRenderingContext2D | null = null;
17
+ private frames: ImageBitmap[] = [];
18
+ private frameRate = 0;
19
+ private transitioning = false;
20
+ private debug: boolean = false;
21
+ private frameThreshold: number = 0;
22
+ private transitionSpeed: number = 10;
23
+ private useWebCodecs: boolean = true;
24
+ private resizeObserver: ResizeObserver;
25
+
26
+ constructor(options: ScrollVideoOptions) {
27
+ this.resizeObserver = new ResizeObserver(() => {
28
+ this.resize();
29
+ });
30
+ const {
31
+ src,
32
+ videoContainer
33
+ } = options;
34
+ if (typeof document !== 'object') {
35
+ console.error('ScrollVideo must be initiated in a DOM context');
36
+ return;
37
+ }
38
+ if (!videoContainer) {
39
+ console.error('scrollVideoContainer must be a valid DOM object');
40
+ return;
41
+ }
42
+ if (!src) {
43
+ console.error('Must provide valid video src to ScrollVideo');
44
+ return;
45
+ }
46
+
47
+ this.container = typeof videoContainer === 'string' ? document.getElementById(videoContainer)! : videoContainer;
48
+ this.resizeObserver.observe(this.container);
49
+ this.video = document.createElement('video');
50
+ this.video.src = src;
51
+ this.video.preload = 'auto';
52
+ this.video.tabIndex = 0;
53
+ this.video.playsInline = true;
54
+ this.video.muted = true;
55
+ this.video.pause();
56
+ this.video.load();
57
+ this.container.appendChild(this.video);
58
+ const browserEngine = new UAParser().getEngine();
59
+ this.isSafari = browserEngine.name === 'WebKit';
60
+ if (this.debug && this.isSafari) console.info('Safari browser detected');
61
+ this.video.addEventListener('loadedmetadata', () => this.setTargetTimePercent(0, true), { once: true });
62
+ this.video.addEventListener('progress', this.resize);
63
+ this.decodeVideo();
64
+ }
65
+
66
+ private setCoverStyle(el: any) {
67
+ if (el && this.container) {
68
+ el.style.position = 'absolute';
69
+ el.style.top = '50%';
70
+ el.style.left = '50%';
71
+ el.style.transform = 'translate(-50%, -50%)';
72
+ const { width: containerWidth, height: containerHeight } = this.container.getBoundingClientRect();
73
+ const width = el.videoWidth || el.width;
74
+ const height = el.videoHeight || el.height;
75
+ if (containerWidth / containerHeight > width / height) {
76
+ el.style.width = '100%';
77
+ el.style.height = 'auto';
78
+ } else {
79
+ el.style.height = '100%';
80
+ el.style.width = 'auto';
81
+ }
82
+ }
83
+ }
84
+
85
+ private resize = () => {
86
+ if (this.debug) console.info('ScrollVideo resizing...');
87
+ if (this.canvas) {
88
+ this.setCoverStyle(this.canvas);
89
+ } else if (this.video) {
90
+ this.setCoverStyle(this.video);
91
+ }
92
+ this.paintCanvasFrame(Math.floor(this.currentTime * this.frameRate));
93
+ };
94
+
95
+ private decodeVideo = () => {
96
+ if (!this.video) return;
97
+ if (this.useWebCodecs && this.video.src) {
98
+ videoDecoder(this.video.src, (frame: ImageBitmap) => {
99
+ this.frames.push(frame);
100
+ },
101
+ this.debug,
102
+ ).then(() => {
103
+ if (!this.video || !this.container) return
104
+ if (this.frames.length === 0) {
105
+ if (this.debug) console.error('No frames were received from webCodecs');
106
+ return;
107
+ }
108
+ this.frameRate = this.frames.length / this.video.duration;
109
+ if (this.debug) console.info('Received', this.frames.length, 'frames');
110
+ this.canvas = document.createElement('canvas');
111
+ this.context = this.canvas.getContext('2d')!;
112
+ this.video.style.display = 'none';
113
+ this.container.appendChild(this.canvas);
114
+ this.paintCanvasFrame(Math.floor(this.currentTime * this.frameRate));
115
+ }).catch(() => {
116
+ if (this.debug) console.error('Error encountered while decoding video');
117
+ this.frames = [];
118
+ this.video!.load();
119
+ });
120
+ }
121
+ }
122
+
123
+ private paintCanvasFrame(frameNum: number) {
124
+ if (this.canvas) {
125
+ const frameIdx = Math.min(frameNum, this.frames.length - 1);
126
+ const currFrame = this.frames[frameIdx];
127
+ if (currFrame && this.container) {
128
+ if (this.debug) console.info('Painting frame', frameIdx);
129
+ this.canvas.width = currFrame.width;
130
+ this.canvas.height = currFrame.height;
131
+ const { width, height } = this.container.getBoundingClientRect();
132
+ this.resetCanvasDimensions(width, height, currFrame.width, currFrame.height);
133
+ this.context!.drawImage(currFrame, 0, 0, currFrame.width, currFrame.height);
134
+ }
135
+ }
136
+ }
137
+
138
+ private transitionToTargetTime(jump: boolean) {
139
+ if (!this.video) return;
140
+ if (this.debug) console.info('Transitioning targetTime:', this.targetTime, 'currentTime:', this.currentTime);
141
+ if (isNaN(this.targetTime) || Math.abs(this.currentTime - this.targetTime) < this.frameThreshold) {
142
+ this.video.pause();
143
+ this.transitioning = false;
144
+ return;
145
+ }
146
+ // Make sure we don't go out of time bounds
147
+ if (this.targetTime > this.video.duration) {
148
+ this.targetTime = this.video.duration;
149
+ }
150
+ if (this.targetTime < 0) {
151
+ this.targetTime = 0;
152
+ }
153
+ // How far forward we need to transition
154
+ const transitionForward = this.targetTime - this.currentTime;
155
+ if (this.canvas) {
156
+ // Update currentTime and paint the closest frame
157
+ this.currentTime += transitionForward / (256 / this.transitionSpeed);
158
+ // If jump, we go directly to the frame
159
+ if (jump) { this.currentTime = this.targetTime }
160
+ this.paintCanvasFrame(Math.floor(this.currentTime * this.frameRate));
161
+ } else if (jump || this.isSafari || this.targetTime - this.currentTime < 0) {
162
+ this.video.pause();
163
+ this.currentTime += transitionForward / (64 / this.transitionSpeed);
164
+ // If jump, we go directly to the frame
165
+ if (jump) { this.currentTime = this.targetTime;}
166
+ this.video.currentTime = this.currentTime;
167
+ } else {
168
+ // Otherwise, we play the video and adjust the playbackRate to get a smoother
169
+ // animation effect.
170
+ const playbackRate = Math.max(Math.min(transitionForward * 4, this.transitionSpeed, 16), 1);
171
+ if (this.debug) console.info('ScrollVideo playbackRate:', playbackRate);
172
+ if (!isNaN(playbackRate)) {
173
+ this.video.playbackRate = playbackRate;
174
+ this.video.play();
175
+ }
176
+ this.currentTime = this.video.currentTime;
177
+ }
178
+ if (typeof requestAnimationFrame === 'function') {
179
+ requestAnimationFrame(() => this.transitionToTargetTime(jump));
180
+ }
181
+ }
182
+
183
+ private resetCanvasDimensions(w: number, h: number, frameW: number, frameH: number) {
184
+ if (!this.canvas) return;
185
+ if (w / h > frameW / frameH) {
186
+ this.canvas.style.width = '100%';
187
+ this.canvas.style.height = 'auto';
188
+ } else {
189
+ this.canvas.style.height = '100%';
190
+ this.canvas.style.width = 'auto';
191
+ }
192
+ }
193
+
194
+ setTargetTimePercent(setPercentage: number, jump: boolean = true): void {
195
+ if (!this.video) return;
196
+ this.targetTime = Math.max(Math.min(setPercentage, 1), 0)
197
+ * (this.frames.length && this.frameRate ? this.frames.length / this.frameRate : this.video.duration);
198
+ if (!jump && Math.abs(this.currentTime - this.targetTime) < this.frameThreshold) return;
199
+ if (!jump && this.transitioning) return;
200
+ if (!this.canvas && !this.video.paused) this.video.play();
201
+ this.transitioning = true;
202
+ this.transitionToTargetTime(jump);
203
+ }
204
+
205
+ destroy(): void {
206
+ this.resizeObserver.unobserve(this.container!);
207
+ this.video?.removeEventListener('progress', this.resize);
208
+ if (this.debug) console.info('Destroying ScrollVideo');
209
+ if (this.container) this.container.innerHTML = '';
210
+ }
211
+ }
@@ -0,0 +1,174 @@
1
+ // @ts-ignore
2
+ import * as MP4Box from 'mp4box';
3
+
4
+ export class Writer {
5
+ private data: Uint8Array;
6
+ private idx: number;
7
+ private size: number;
8
+
9
+ constructor(size: number) {
10
+ this.data = new Uint8Array(size);
11
+ this.idx = 0;
12
+ this.size = size;
13
+ }
14
+
15
+ getData() {
16
+ if (this.idx !== this.size) throw new Error('Mismatch between size reserved and sized used');
17
+ return this.data.slice(0, this.idx);
18
+ }
19
+
20
+ writeUint8(value: number) {
21
+ this.data.set([value], this.idx);
22
+ this.idx += 1;
23
+ }
24
+
25
+ writeUint16(value: number) {
26
+ const arr = new Uint16Array(1);
27
+ arr[0] = value;
28
+ const buffer = new Uint8Array(arr.buffer);
29
+ this.data.set([buffer[1], buffer[0]], this.idx);
30
+ this.idx += 2;
31
+ }
32
+
33
+ writeUint8Array(value: Uint8Array) {
34
+ this.data.set(value, this.idx);
35
+ this.idx += value.length;
36
+ }
37
+ }
38
+
39
+ interface AVCCBox {
40
+ configurationVersion: number;
41
+ AVCProfileIndication: number;
42
+ profile_compatibility: number;
43
+ AVCLevelIndication: number;
44
+ lengthSizeMinusOne: number;
45
+ nb_SPS_nalus: number;
46
+ SPS: { length: number; nalu: Uint8Array }[];
47
+ nb_PPS_nalus: number;
48
+ PPS: { length: number; nalu: Uint8Array }[];
49
+ }
50
+
51
+ const getExtradata = (avccBox: AVCCBox) => {
52
+ let i;
53
+ let size = 7;
54
+ for (i = 0; i < avccBox.SPS.length; i += 1) {
55
+ // nalu length is encoded as a uint16.
56
+ size += 2 + avccBox.SPS[i].length;
57
+ }
58
+ for (i = 0; i < avccBox.PPS.length; i += 1) {
59
+ // nalu length is encoded as a uint16.
60
+ size += 2 + avccBox.PPS[i].length;
61
+ }
62
+ const writer = new Writer(size);
63
+ writer.writeUint8(avccBox.configurationVersion);
64
+ writer.writeUint8(avccBox.AVCProfileIndication);
65
+ writer.writeUint8(avccBox.profile_compatibility);
66
+ writer.writeUint8(avccBox.AVCLevelIndication);
67
+ writer.writeUint8(avccBox.lengthSizeMinusOne + (63 << 2));
68
+ writer.writeUint8(avccBox.nb_SPS_nalus + (7 << 5));
69
+ for (i = 0; i < avccBox.SPS.length; i += 1) {
70
+ writer.writeUint16(avccBox.SPS[i].length);
71
+ writer.writeUint8Array(avccBox.SPS[i].nalu);
72
+ }
73
+ writer.writeUint8(avccBox.nb_PPS_nalus);
74
+ for (i = 0; i < avccBox.PPS.length; i += 1) {
75
+ writer.writeUint16(avccBox.PPS[i].length);
76
+ writer.writeUint8Array(avccBox.PPS[i].nalu);
77
+ }
78
+ return writer.getData();
79
+ };
80
+
81
+ const decodeVideo = (
82
+ src: string,
83
+ emitFrame: (bitmap: ImageBitmap) => void,
84
+ { VideoDecoder, EncodedVideoChunk, debug }: any
85
+ ): Promise<void> =>
86
+ new Promise((resolve, reject) => {
87
+ if (debug) console.info('Decoding video from', src);
88
+ try {
89
+ const mp4boxfile = MP4Box.createFile();
90
+ let codec;
91
+ const decoder = new VideoDecoder({
92
+ output: (frame: VideoFrame) => {
93
+ console.log(frame);
94
+ createImageBitmap(frame, { resizeQuality: 'low' }).then((bitmap) => {
95
+ emitFrame(bitmap);
96
+ frame.close();
97
+ if (decoder.decodeQueueSize <= 0) {
98
+ setTimeout(() => {
99
+ if (decoder.state !== 'closed') {
100
+ decoder.close();
101
+ resolve();
102
+ }
103
+ }, 500);
104
+ }
105
+ });
106
+ },
107
+ error: (e: any) => {
108
+ console.error(e);
109
+ reject(e);
110
+ },
111
+ });
112
+
113
+ mp4boxfile.onReady = (info: any) => {
114
+ if (info && info.videoTracks && info.videoTracks[0]) {
115
+ [{ codec }] = info.videoTracks;
116
+ if (debug) console.info('Video with codec:', codec);
117
+ const avccBox = mp4boxfile.moov.traks[0].mdia.minf.stbl.stsd.entries[0].avcC;
118
+ const extradata = getExtradata(avccBox);
119
+ decoder.configure({ codec, description: extradata });
120
+ mp4boxfile.setExtractionOptions(info.videoTracks[0].id);
121
+ mp4boxfile.start();
122
+ } else reject(new Error('URL provided is not a valid mp4 video file.'));
123
+ };
124
+
125
+ mp4boxfile.onSamples = (track_id: number, ref: any, samples: any[]) => {
126
+ for (let i = 0; i < samples.length; i += 1) {
127
+ const sample = samples[i];
128
+ const type = sample.is_sync ? 'key' : 'delta';
129
+ const chunk = new EncodedVideoChunk({
130
+ type,
131
+ timestamp: sample.cts,
132
+ duration: sample.duration,
133
+ data: sample.data,
134
+ });
135
+ decoder.decode(chunk);
136
+ }
137
+ };
138
+
139
+ fetch(src).then((res) => {
140
+ const reader = res.body!.getReader();
141
+ let offset = 0;
142
+ //@ts-ignore
143
+ function appendBuffers({ done, value }: any) {
144
+ if (done) {
145
+ mp4boxfile.flush();
146
+ return null;
147
+ }
148
+ const buf = value.buffer;
149
+ buf.fileStart = offset;
150
+ offset += buf.byteLength;
151
+ mp4boxfile.appendBuffer(buf);
152
+ return reader.read().then(appendBuffers);
153
+ }
154
+ return reader.read().then(appendBuffers);
155
+ });
156
+ } catch (e) {
157
+ reject(e);
158
+ }
159
+ });
160
+
161
+
162
+ export default ( src: string, emitFrame: (bitmap: ImageBitmap) => void, debug: boolean): Promise<void> => {
163
+ if (typeof VideoDecoder === 'function' && typeof EncodedVideoChunk === 'function') {
164
+ if (debug)
165
+ console.info('WebCodecs is natively supported, using native version...');
166
+ return decodeVideo(src, emitFrame, {
167
+ VideoDecoder,
168
+ EncodedVideoChunk,
169
+ debug,
170
+ });
171
+ }
172
+ if (debug) console.info('WebCodecs is not available in this browser.');
173
+ return Promise.resolve();
174
+ };
@@ -1,5 +0,0 @@
1
- <component name="ProjectCodeStyleConfiguration">
2
- <state>
3
- <option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
4
- </state>
5
- </component>
@@ -1,15 +0,0 @@
1
- <component name="InspectionProjectProfileManager">
2
- <profile version="1.0">
3
- <option name="myName" value="Project Default" />
4
- <inspection_tool class="HtmlUnknownAttribute" enabled="true" level="WARNING" enabled_by_default="true">
5
- <option name="myValues">
6
- <value>
7
- <list size="1">
8
- <item index="0" class="java.lang.String" itemvalue="jsx" />
9
- </list>
10
- </value>
11
- </option>
12
- <option name="myCustomValuesEnabled" value="true" />
13
- </inspection_tool>
14
- </profile>
15
- </component>