@reuters-graphics/graphics-components 3.0.12 → 3.0.13

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 (75) hide show
  1. package/dist/components/@types/global.d.ts +45 -0
  2. package/dist/components/Scroller/Scroller.mdx +2 -2
  3. package/dist/components/ScrollerBase/ScrollerBase.mdx +0 -5
  4. package/dist/components/ScrollerVideo/Debug.svelte +207 -0
  5. package/dist/components/ScrollerVideo/Debug.svelte.d.ts +5 -0
  6. package/dist/components/ScrollerVideo/ScrollerVideo.mdx +462 -0
  7. package/dist/components/ScrollerVideo/ScrollerVideo.stories.svelte +190 -0
  8. package/dist/components/ScrollerVideo/ScrollerVideo.stories.svelte.d.ts +19 -0
  9. package/dist/components/ScrollerVideo/ScrollerVideo.svelte +292 -0
  10. package/dist/components/ScrollerVideo/ScrollerVideo.svelte.d.ts +58 -0
  11. package/dist/components/ScrollerVideo/ScrollerVideoForeground.svelte +164 -0
  12. package/dist/components/ScrollerVideo/ScrollerVideoForeground.svelte.d.ts +17 -0
  13. package/dist/components/ScrollerVideo/demo/AdvancedUsecases.svelte +114 -0
  14. package/dist/components/ScrollerVideo/demo/AdvancedUsecases.svelte.d.ts +3 -0
  15. package/dist/components/ScrollerVideo/demo/Embedded.svelte +94 -0
  16. package/dist/components/ScrollerVideo/demo/Embedded.svelte.d.ts +3 -0
  17. package/dist/components/ScrollerVideo/demo/WithAi2svelteForegrounds.svelte +117 -0
  18. package/dist/components/ScrollerVideo/demo/WithAi2svelteForegrounds.svelte.d.ts +3 -0
  19. package/dist/components/ScrollerVideo/demo/WithScrollerBase.svelte +80 -0
  20. package/dist/components/ScrollerVideo/demo/WithScrollerBase.svelte.d.ts +3 -0
  21. package/dist/components/ScrollerVideo/demo/WithTextForegrounds.svelte +72 -0
  22. package/dist/components/ScrollerVideo/demo/WithTextForegrounds.svelte.d.ts +18 -0
  23. package/dist/components/ScrollerVideo/demo/graphic/ai2svelte/ai-chart.svelte +631 -0
  24. package/dist/components/ScrollerVideo/demo/graphic/ai2svelte/ai-chart.svelte.d.ts +3 -0
  25. package/dist/components/ScrollerVideo/demo/graphic/ai2svelte/annotation1.svelte +428 -0
  26. package/dist/components/ScrollerVideo/demo/graphic/ai2svelte/annotation1.svelte.d.ts +26 -0
  27. package/dist/components/ScrollerVideo/demo/graphic/ai2svelte/annotation2.svelte +402 -0
  28. package/dist/components/ScrollerVideo/demo/graphic/ai2svelte/annotation2.svelte.d.ts +26 -0
  29. package/dist/components/ScrollerVideo/demo/graphic/ai2svelte/annotation3.svelte +398 -0
  30. package/dist/components/ScrollerVideo/demo/graphic/ai2svelte/annotation3.svelte.d.ts +26 -0
  31. package/dist/components/ScrollerVideo/demo/graphic/ai2svelte/annotation4.svelte +360 -0
  32. package/dist/components/ScrollerVideo/demo/graphic/ai2svelte/annotation4.svelte.d.ts +26 -0
  33. package/dist/components/ScrollerVideo/demo/graphic/imgs/ai-chart-md.png +0 -0
  34. package/dist/components/ScrollerVideo/demo/graphic/imgs/ai-chart-sm.png +0 -0
  35. package/dist/components/ScrollerVideo/demo/graphic/imgs/ai-chart-xs.png +0 -0
  36. package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation1-lg.png +0 -0
  37. package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation1-md.png +0 -0
  38. package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation1-sm.png +0 -0
  39. package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation1-xl.png +0 -0
  40. package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation1-xs.png +0 -0
  41. package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation2-lg.png +0 -0
  42. package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation2-md.png +0 -0
  43. package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation2-sm.png +0 -0
  44. package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation2-xl.png +0 -0
  45. package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation2-xs.png +0 -0
  46. package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation3-lg.png +0 -0
  47. package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation3-md.png +0 -0
  48. package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation3-sm.png +0 -0
  49. package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation3-xl.png +0 -0
  50. package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation3-xs.png +0 -0
  51. package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation4-lg.png +0 -0
  52. package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation4-md.png +0 -0
  53. package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation4-sm.png +0 -0
  54. package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation4-xl.png +0 -0
  55. package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation4-xs.png +0 -0
  56. package/dist/components/ScrollerVideo/ts/ScrollerVideo.d.ts +248 -0
  57. package/dist/components/ScrollerVideo/ts/ScrollerVideo.js +762 -0
  58. package/dist/components/ScrollerVideo/ts/mp4box.d.ts +137 -0
  59. package/dist/components/ScrollerVideo/ts/state.svelte.d.ts +51 -0
  60. package/dist/components/ScrollerVideo/ts/state.svelte.js +25 -0
  61. package/dist/components/ScrollerVideo/ts/utils.d.ts +70 -0
  62. package/dist/components/ScrollerVideo/ts/utils.js +92 -0
  63. package/dist/components/ScrollerVideo/ts/videoDecoder.d.ts +11 -0
  64. package/dist/components/ScrollerVideo/ts/videoDecoder.js +193 -0
  65. package/dist/components/ScrollerVideo/videos/HPO.mp4 +0 -0
  66. package/dist/components/ScrollerVideo/videos/drone.mp4 +0 -0
  67. package/dist/components/ScrollerVideo/videos/goldengate.mp4 +0 -0
  68. package/dist/components/ScrollerVideo/videos/tennis.mp4 +0 -0
  69. package/dist/components/ScrollerVideo/videos/waves_lg.mp4 +0 -0
  70. package/dist/components/ScrollerVideo/videos/waves_md.mp4 +0 -0
  71. package/dist/components/ScrollerVideo/videos/waves_sm.mp4 +0 -0
  72. package/dist/components/SiteHeadline/SiteHeadline.mdx +4 -1
  73. package/dist/index.d.ts +3 -1
  74. package/dist/index.js +2 -0
  75. package/package.json +3 -1
@@ -0,0 +1,137 @@
1
+ declare module 'mp4box' {
2
+ export interface MP4MediaTrack {
3
+ id: number;
4
+ created: Date;
5
+ modified: Date;
6
+ movie_duration: number;
7
+ movie_timescale: number;
8
+ layer: number;
9
+ alternate_group: number;
10
+ volume: number;
11
+ track_width: number;
12
+ track_height: number;
13
+ timescale: number;
14
+ duration: number;
15
+ bitrate: number;
16
+ codec: string;
17
+ language: string;
18
+ nb_samples: number;
19
+ }
20
+
21
+ export interface MP4VideoData {
22
+ width: number;
23
+ height: number;
24
+ }
25
+
26
+ export interface MP4VideoTrack extends MP4MediaTrack {
27
+ video: MP4VideoData;
28
+ }
29
+
30
+ export interface MP4AudioData {
31
+ sample_rate: number;
32
+ channel_count: number;
33
+ sample_size: number;
34
+ }
35
+
36
+ export interface MP4AudioTrack extends MP4MediaTrack {
37
+ audio: MP4AudioData;
38
+ }
39
+
40
+ export type MP4Track = MP4VideoTrack | MP4AudioTrack;
41
+
42
+ export interface MP4Info {
43
+ duration: number;
44
+ timescale: number;
45
+ fragment_duration: number;
46
+ isFragmented: boolean;
47
+ isProgressive: boolean;
48
+ hasIOD: boolean;
49
+ brands: string[];
50
+ created: Date;
51
+ modified: Date;
52
+ tracks: MP4Track[];
53
+ audioTracks: MP4AudioTrack[];
54
+ videoTracks: MP4VideoTrack[];
55
+ }
56
+
57
+ export interface MP4Sample {
58
+ alreadyRead: number;
59
+ chunk_index: number;
60
+ chunk_run_index: number;
61
+ cts: number;
62
+ data: Uint8Array;
63
+ degradation_priority: number;
64
+ depends_on: number;
65
+ description: unknown;
66
+ description_index: number;
67
+ dts: number;
68
+ duration: number;
69
+ has_redundancy: number;
70
+ is_depended_on: number;
71
+ is_leading: number;
72
+ is_sync: boolean;
73
+ number: number;
74
+ offset: number;
75
+ size: number;
76
+ timescale: number;
77
+ track_id: number;
78
+ }
79
+
80
+ export type MP4ArrayBuffer = ArrayBuffer & { fileStart: number };
81
+
82
+ export class DataStream {
83
+ static BIG_ENDIAN: boolean;
84
+ static LITTLE_ENDIAN: boolean;
85
+ buffer: ArrayBuffer;
86
+ constructor(
87
+ arrayBuffer?: ArrayBuffer,
88
+ byteOffset: number,
89
+ endianness: boolean
90
+ ): void;
91
+ // TODO: Complete interface
92
+ }
93
+
94
+ export interface Trak {
95
+ mdia?: {
96
+ minf?: {
97
+ stbl?: {
98
+ stsd?: {
99
+ entries: {
100
+ avcC?: {
101
+ write: (stream: DataStream) => void;
102
+ };
103
+ hvcC?: {
104
+ write: (stream: DataStream) => void;
105
+ };
106
+ }[];
107
+ };
108
+ };
109
+ };
110
+ };
111
+ // TODO: Complete interface
112
+ }
113
+
114
+ export interface MP4File {
115
+ [x: string]: unknown;
116
+ onMoovStart?: () => void;
117
+ onReady?: (info: MP4Info) => void;
118
+ onError?: (e: string) => void;
119
+ onSamples?: (id: number, user: unknown, samples: MP4Sample[]) => unknown;
120
+
121
+ appendBuffer(data: MP4ArrayBuffer): number;
122
+ start(): void;
123
+ stop(): void;
124
+ flush(): void;
125
+ releaseUsedSamples(trackId: number, sampleNumber: number): void;
126
+ setExtractionOptions(
127
+ trackId: number,
128
+ user?: unknown,
129
+ options?: { nbSamples?: number; rapAlignment?: number }
130
+ ): void;
131
+ getTrackById(trackId: number): Trak;
132
+ }
133
+
134
+ export function createFile(): MP4File;
135
+
136
+ export {};
137
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * General video data for ScrollerVideo state.
3
+ * @typedef {Object} GeneralData
4
+ * @property {string} src - Video source URL.
5
+ * @property {number} videoPercentage - Current video percentage (0-1).
6
+ * @property {number} frameRate - Video frame rate.
7
+ * @property {number} currentTime - Current video time in seconds.
8
+ * @property {number} totalTime - Total video duration in seconds.
9
+ */
10
+ type GeneralData = {
11
+ src: string;
12
+ videoPercentage: number;
13
+ frameRate: number;
14
+ currentTime: number;
15
+ totalTime: number;
16
+ };
17
+ /**
18
+ * Frame-level data for ScrollerVideo state.
19
+ * @typedef {Object} FramesData
20
+ * @property {string} codec - Video codec string.
21
+ * @property {number} currentFrame - Current frame index.
22
+ * @property {number} totalFrames - Total number of frames.
23
+ */
24
+ type FramesData = {
25
+ codec: string;
26
+ currentFrame: number;
27
+ totalFrames: number;
28
+ };
29
+ /**
30
+ * State object for ScrollerVideo component.
31
+ * @typedef {Object} ScrollerVideoState
32
+ * @property {GeneralData} generalData - General video data.
33
+ * @property {boolean} usingWebCodecs - Whether WebCodecs is used.
34
+ * @property {FramesData} framesData - Frame-level data.
35
+ * @property {boolean} isAutoPlaying - Whether video is autoplaying.
36
+ * @property {number} autoplayProgress - Progress of autoplay (0-1).
37
+ */
38
+ export type ScrollerVideoState = {
39
+ generalData: GeneralData;
40
+ usingWebCodecs: boolean;
41
+ framesData: FramesData;
42
+ willAutoPlay: boolean;
43
+ isAutoPlaying: boolean;
44
+ autoplayProgress: number;
45
+ };
46
+ /**
47
+ * Creates a new ScrollerVideoState object with default values.
48
+ * @returns {ScrollerVideoState} The initialized state object.
49
+ */
50
+ export declare function createComponentState(): ScrollerVideoState;
51
+ export {};
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Creates a new ScrollerVideoState object with default values.
3
+ * @returns {ScrollerVideoState} The initialized state object.
4
+ */
5
+ export function createComponentState() {
6
+ const scrollerVideoState = $state({
7
+ generalData: {
8
+ src: '',
9
+ videoPercentage: 0,
10
+ frameRate: 0,
11
+ currentTime: 0,
12
+ totalTime: 0,
13
+ },
14
+ usingWebCodecs: false,
15
+ framesData: {
16
+ codec: '',
17
+ currentFrame: 0,
18
+ totalFrames: 0,
19
+ },
20
+ willAutoPlay: false,
21
+ isAutoPlaying: false,
22
+ autoplayProgress: 0,
23
+ });
24
+ return scrollerVideoState;
25
+ }
@@ -0,0 +1,70 @@
1
+ import type { ScrollerVideoState } from './state.svelte';
2
+ /**
3
+ * Flattened version of ScrollerVideoState for easier access to all properties.
4
+ * @typedef {Object} FlattenedScrollerVideoState
5
+ * @property {string} src - Video source URL.
6
+ * @property {number} videoPercentage - Current video percentage (0-1).
7
+ * @property {number} frameRate - Video frame rate.
8
+ * @property {number} currentTime - Current video time in seconds.
9
+ * @property {number} totalTime - Total video duration in seconds.
10
+ * @property {boolean} usingWebCodecs - Whether WebCodecs is used.
11
+ * @property {string} codec - Video codec string.
12
+ * @property {number} currentFrame - Current frame index.
13
+ * @property {number} totalFrames - Total number of frames.
14
+ * @property {boolean} isAutoPlaying - Whether video is autoplaying.
15
+ * @property {number} autoplayProgress - Progress of autoplay (0-1).
16
+ */
17
+ type FlattenedScrollerVideoState = {
18
+ src: string;
19
+ videoPercentage: number;
20
+ frameRate: number;
21
+ currentTime: number;
22
+ totalTime: number;
23
+ usingWebCodecs: boolean;
24
+ codec: string;
25
+ currentFrame: number;
26
+ totalFrames: number;
27
+ isAutoPlaying: boolean;
28
+ autoplayProgress: number;
29
+ };
30
+ /**
31
+ * Returns a debounced version of the given function.
32
+ * @template T
33
+ * @param {T} func - The function to debounce.
34
+ * @param {number} [delay=0] - The debounce delay in milliseconds.
35
+ * @returns {(...args: Parameters<T>) => void} The debounced function.
36
+ */
37
+ export declare function debounce<T extends (...args: unknown[]) => void>(func: T, delay?: number): (...args: Parameters<T>) => void;
38
+ /**
39
+ * Checks if the current scroll position is at the target position within a threshold.
40
+ * @param {number} targetScrollPosition - The target scroll position in pixels.
41
+ * @param {number} [threshold=1] - The allowed threshold in pixels.
42
+ * @returns {boolean} True if the current scroll position is within the threshold of the target.
43
+ */
44
+ export declare const isScrollPositionAtTarget: (targetScrollPosition: number, threshold?: number) => boolean;
45
+ /**
46
+ * Constrains a number between a lower and upper bound.
47
+ * @param {number} n - The number to constrain.
48
+ * @param {number} low - The lower bound.
49
+ * @param {number} high - The upper bound.
50
+ * @returns {number} The constrained value.
51
+ */
52
+ export declare function constrain(n: number, low: number, high: number): number;
53
+ /**
54
+ * Maps a number from one range to another.
55
+ * @param {number} n - The number to map.
56
+ * @param {number} start1 - Lower bound of the value's current range.
57
+ * @param {number} stop1 - Upper bound of the value's current range.
58
+ * @param {number} start2 - Lower bound of the value's target range.
59
+ * @param {number} stop2 - Upper bound of the value's target range.
60
+ * @param {boolean} [withinBounds=true] - Whether to constrain the result within the target range.
61
+ * @returns {number} The mapped value.
62
+ */
63
+ export declare function map(n: number, start1: number, stop1: number, start2: number, stop2: number, withinBounds?: boolean): number;
64
+ /**
65
+ * Flattens a ScrollerVideoState object into a single-level object for easier access.
66
+ * @param {ScrollerVideoState} obj - The state object to flatten.
67
+ * @returns {FlattenedScrollerVideoState} The flattened state object.
68
+ */
69
+ export declare function flattenObject(obj: ScrollerVideoState): FlattenedScrollerVideoState;
70
+ export {};
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Returns a debounced version of the given function.
3
+ * @template T
4
+ * @param {T} func - The function to debounce.
5
+ * @param {number} [delay=0] - The debounce delay in milliseconds.
6
+ * @returns {(...args: Parameters<T>) => void} The debounced function.
7
+ */
8
+ export function debounce(func, delay = 0) {
9
+ let timeoutId;
10
+ return (...args) => {
11
+ clearTimeout(timeoutId);
12
+ timeoutId = setTimeout(() => {
13
+ func(...args);
14
+ }, delay);
15
+ };
16
+ }
17
+ /**
18
+ * Checks if the current scroll position is at the target position within a threshold.
19
+ * @param {number} targetScrollPosition - The target scroll position in pixels.
20
+ * @param {number} [threshold=1] - The allowed threshold in pixels.
21
+ * @returns {boolean} True if the current scroll position is within the threshold of the target.
22
+ */
23
+ export const isScrollPositionAtTarget = (targetScrollPosition, threshold = 1) => {
24
+ const currentScrollPosition = window.pageYOffset;
25
+ const difference = Math.abs(currentScrollPosition - targetScrollPosition);
26
+ return difference < threshold;
27
+ };
28
+ /**
29
+ * Constrains a number between a lower and upper bound.
30
+ * @param {number} n - The number to constrain.
31
+ * @param {number} low - The lower bound.
32
+ * @param {number} high - The upper bound.
33
+ * @returns {number} The constrained value.
34
+ */
35
+ export function constrain(n, low, high) {
36
+ return Math.max(Math.min(n, high), low);
37
+ }
38
+ /**
39
+ * Maps a number from one range to another.
40
+ * @param {number} n - The number to map.
41
+ * @param {number} start1 - Lower bound of the value's current range.
42
+ * @param {number} stop1 - Upper bound of the value's current range.
43
+ * @param {number} start2 - Lower bound of the value's target range.
44
+ * @param {number} stop2 - Upper bound of the value's target range.
45
+ * @param {boolean} [withinBounds=true] - Whether to constrain the result within the target range.
46
+ * @returns {number} The mapped value.
47
+ */
48
+ export function map(n, start1, stop1, start2, stop2, withinBounds = true) {
49
+ const newval = ((n - start1) / (stop1 - start1)) * (stop2 - start2) + start2;
50
+ if (!withinBounds) {
51
+ return newval;
52
+ }
53
+ if (start2 < stop2) {
54
+ return constrain(newval, start2, stop2);
55
+ }
56
+ else {
57
+ return constrain(newval, stop2, start2);
58
+ }
59
+ }
60
+ /**
61
+ * Flattens a ScrollerVideoState object into a single-level object for easier access.
62
+ * @param {ScrollerVideoState} obj - The state object to flatten.
63
+ * @returns {FlattenedScrollerVideoState} The flattened state object.
64
+ */
65
+ export function flattenObject(obj) {
66
+ const result = {};
67
+ function flatten(current, property) {
68
+ if (Object(current) !== current) {
69
+ result[property] = current;
70
+ }
71
+ else if (Array.isArray(current)) {
72
+ for (let i = 0, l = current.length; i < l; i++) {
73
+ flatten(current[i], property + '[' + i + ']');
74
+ if (l === 0) {
75
+ result[property] = [];
76
+ }
77
+ }
78
+ }
79
+ else if (typeof current === 'object') {
80
+ let isEmpty = true;
81
+ for (const p in current) {
82
+ isEmpty = false;
83
+ flatten(current[p], p);
84
+ }
85
+ if (isEmpty && property) {
86
+ result[property] = {};
87
+ }
88
+ }
89
+ }
90
+ flatten(obj, '');
91
+ return result;
92
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The main function for decoding video. Deals with the polyfill cases first,
3
+ * then calls our decodeVideo.
4
+ *
5
+ * @param src
6
+ * @param emitFrame
7
+ * @param debug
8
+ * @returns {Promise<never>|Promise<void>}
9
+ */
10
+ declare const _default: (src: string, emitFrame: (frame: ImageBitmap) => void, debug?: boolean) => Promise<unknown>;
11
+ export default _default;
@@ -0,0 +1,193 @@
1
+ import * as MP4Box from 'mp4box';
2
+ /**
3
+ * Taken from https://github.com/w3c/webcodecs/blob/main/samples/mp4-decode/mp4_demuxer.js
4
+ */
5
+ class Writer {
6
+ data;
7
+ idx;
8
+ size;
9
+ constructor(size) {
10
+ this.data = new Uint8Array(size);
11
+ this.idx = 0;
12
+ this.size = size;
13
+ }
14
+ getData() {
15
+ if (this.idx !== this.size)
16
+ throw new Error('Mismatch between size reserved and sized used');
17
+ return this.data.slice(0, this.idx);
18
+ }
19
+ writeUint8(value) {
20
+ this.data.set([value], this.idx);
21
+ this.idx += 1;
22
+ }
23
+ writeUint16(value) {
24
+ const arr = new Uint16Array(1);
25
+ arr[0] = value;
26
+ const buffer = new Uint8Array(arr.buffer);
27
+ this.data.set([buffer[1], buffer[0]], this.idx);
28
+ this.idx += 2;
29
+ }
30
+ writeUint8Array(value) {
31
+ this.data.set(value, this.idx);
32
+ this.idx += value.length;
33
+ }
34
+ }
35
+ const getExtradata = (avccBox) => {
36
+ let i;
37
+ let size = 7;
38
+ for (i = 0; i < avccBox.SPS.length; i += 1) {
39
+ // nalu length is encoded as a uint16.
40
+ size += 2 + avccBox.SPS[i].length;
41
+ }
42
+ for (i = 0; i < avccBox.PPS.length; i += 1) {
43
+ // nalu length is encoded as a uint16.
44
+ size += 2 + avccBox.PPS[i].length;
45
+ }
46
+ const writer = new Writer(size);
47
+ writer.writeUint8(avccBox.configurationVersion);
48
+ writer.writeUint8(avccBox.AVCProfileIndication);
49
+ writer.writeUint8(avccBox.profile_compatibility);
50
+ writer.writeUint8(avccBox.AVCLevelIndication);
51
+ writer.writeUint8(avccBox.lengthSizeMinusOne + (63 << 2));
52
+ writer.writeUint8(avccBox.nb_SPS_nalus + (7 << 5));
53
+ for (i = 0; i < avccBox.SPS.length; i += 1) {
54
+ writer.writeUint16(avccBox.SPS[i].length);
55
+ writer.writeUint8Array(avccBox.SPS[i].nalu);
56
+ }
57
+ writer.writeUint8(avccBox.nb_PPS_nalus);
58
+ for (i = 0; i < avccBox.PPS.length; i += 1) {
59
+ writer.writeUint16(avccBox.PPS[i].length);
60
+ writer.writeUint8Array(avccBox.PPS[i].nalu);
61
+ }
62
+ return writer.getData();
63
+ };
64
+ /**
65
+ * decodeVideo takes an url to a mp4 file and converts it into frames.
66
+ *
67
+ * The steps for this are:
68
+ * 1. Determine the codec for this video file and demux it into chunks.
69
+ * 2. Read the chunks with VideoDecoder as fast as possible.
70
+ * 3. Return an array of frames that we can efficiently draw to a canvas.
71
+ *
72
+ * @param src
73
+ * @param VideoDecoder
74
+ * @param EncodedVideoChunk
75
+ * @param emitFrame
76
+ * @param debug
77
+ * @returns {Promise<unknown>}
78
+ */
79
+ const decodeVideo = (src, emitFrame, { VideoDecoder, EncodedVideoChunk, debug, }) => new Promise((resolve, reject) => {
80
+ if (debug)
81
+ console.info('Decoding video from', src);
82
+ try {
83
+ // Uses mp4box for demuxing
84
+ const mp4boxfile = MP4Box.createFile();
85
+ // Holds the codec value
86
+ let codec = 'N/A';
87
+ // Creates a VideoDecoder instance
88
+ const decoder = new VideoDecoder({
89
+ output: (frame) => {
90
+ createImageBitmap(frame, { resizeQuality: 'high' }).then((bitmap) => {
91
+ emitFrame(bitmap);
92
+ frame.close();
93
+ if (decoder.decodeQueueSize <= 0) {
94
+ // Give it an extra half second to finish everything
95
+ setTimeout(() => {
96
+ if (decoder.state !== 'closed') {
97
+ decoder.close();
98
+ resolve(codec);
99
+ }
100
+ }, 500);
101
+ }
102
+ });
103
+ },
104
+ error: (e) => {
105
+ console.error(e);
106
+ reject(e);
107
+ },
108
+ });
109
+ mp4boxfile.onReady = (info) => {
110
+ if (info && info.videoTracks && info.videoTracks[0]) {
111
+ [{ codec }] = info.videoTracks;
112
+ if (debug)
113
+ console.info('Video with codec:', codec);
114
+ // Gets the avccbox used for reading extradata
115
+ const moov = mp4boxfile.moov;
116
+ const avccBox = moov?.traks[0].mdia.minf.stbl.stsd.entries[0].avcC;
117
+ if (!avccBox) {
118
+ reject(new Error('Could not find avcC box for extradata.'));
119
+ return;
120
+ }
121
+ const extradata = getExtradata(avccBox);
122
+ // configure decoder
123
+ decoder.configure({ codec, description: extradata });
124
+ // Setup mp4box file for breaking it into chunks
125
+ mp4boxfile.setExtractionOptions(info.videoTracks[0].id);
126
+ mp4boxfile.start();
127
+ }
128
+ else
129
+ reject(new Error('URL provided is not a valid mp4 video file.'));
130
+ };
131
+ mp4boxfile.onSamples = (_track_id, _ref, samples) => {
132
+ for (let i = 0; i < samples.length; i += 1) {
133
+ const sample = samples[i];
134
+ const type = sample.is_sync ? 'key' : 'delta';
135
+ const chunk = new EncodedVideoChunk({
136
+ type,
137
+ timestamp: sample.cts,
138
+ duration: sample.duration,
139
+ data: sample.data,
140
+ });
141
+ decoder.decode(chunk);
142
+ }
143
+ };
144
+ // Fetches the file into arraybuffers
145
+ fetch(src).then((res) => {
146
+ if (!res.body)
147
+ throw new Error('Response body is null');
148
+ const reader = res.body.getReader();
149
+ let offset = 0;
150
+ function appendBuffers(result) {
151
+ if (result.done) {
152
+ mp4boxfile.flush();
153
+ return Promise.resolve();
154
+ }
155
+ const buf = result.value.buffer;
156
+ buf.fileStart = offset;
157
+ offset += buf.byteLength;
158
+ mp4boxfile.appendBuffer(buf);
159
+ return reader.read().then(appendBuffers);
160
+ }
161
+ return reader.read().then(appendBuffers);
162
+ });
163
+ }
164
+ catch (e) {
165
+ reject(e);
166
+ }
167
+ });
168
+ /**
169
+ * The main function for decoding video. Deals with the polyfill cases first,
170
+ * then calls our decodeVideo.
171
+ *
172
+ * @param src
173
+ * @param emitFrame
174
+ * @param debug
175
+ * @returns {Promise<never>|Promise<void>}
176
+ */
177
+ export default (src, emitFrame, debug = false) => {
178
+ // If our browser supports WebCodecs natively
179
+ if (typeof VideoDecoder === 'function' &&
180
+ typeof EncodedVideoChunk === 'function') {
181
+ if (debug)
182
+ console.info('WebCodecs is natively supported, using native version...');
183
+ return decodeVideo(src, emitFrame, {
184
+ VideoDecoder,
185
+ EncodedVideoChunk,
186
+ debug,
187
+ });
188
+ }
189
+ // Otherwise, resolve nothing
190
+ if (debug)
191
+ console.info('WebCodecs is not available in this browser.');
192
+ return Promise.resolve('N/A');
193
+ };
@@ -39,7 +39,10 @@ With the graphics kit, you'll likely get your text value from an ArchieML doc...
39
39
  section: Global News # Optional
40
40
  sectionUrl: https://www.reuters.com/graphics/ # Optional
41
41
  hed: A beautiful page
42
- authors: Samuel Granados, Dea Bankova
42
+ [authors]
43
+ * Samuel Granados
44
+ * Dea Bankova
45
+ []
43
46
  published: 2022-09-12T08:30:00.000Z
44
47
  updated:
45
48
  ```
package/dist/index.d.ts CHANGED
@@ -36,10 +36,12 @@ export { default as SiteHeader } from './components/SiteHeader/SiteHeader.svelte
36
36
  export { default as SiteHeadline } from './components/SiteHeadline/SiteHeadline.svelte';
37
37
  export { default as Spinner } from './components/Spinner/Spinner.svelte';
38
38
  export { default as ScrollerBase } from './components/ScrollerBase/ScrollerBase.svelte';
39
+ export { default as ScrollerVideo } from './components/ScrollerVideo/ScrollerVideo.svelte';
40
+ export { default as ScrollerVideoForeground } from './components/ScrollerVideo/ScrollerVideoForeground.svelte';
39
41
  export { default as SponsorshipAd } from './components/AdSlot/SponsorshipAd.svelte';
40
42
  export { default as Table } from './components/Table/Table.svelte';
41
43
  export { default as Theme, themes } from './components/Theme/Theme.svelte';
42
44
  export { default as ToolsHeader } from './components/ToolsHeader/ToolsHeader.svelte';
43
45
  export { default as Video } from './components/Video/Video.svelte';
44
46
  export { default as Visible } from './components/Visible/Visible.svelte';
45
- export type { ContainerWidth, HeadlineSize } from './components/@types/global';
47
+ export type { ContainerWidth, HeadlineSize, ScrollerVideoInstance, } from './components/@types/global';
package/dist/index.js CHANGED
@@ -38,6 +38,8 @@ export { default as SiteHeader } from './components/SiteHeader/SiteHeader.svelte
38
38
  export { default as SiteHeadline } from './components/SiteHeadline/SiteHeadline.svelte';
39
39
  export { default as Spinner } from './components/Spinner/Spinner.svelte';
40
40
  export { default as ScrollerBase } from './components/ScrollerBase/ScrollerBase.svelte';
41
+ export { default as ScrollerVideo } from './components/ScrollerVideo/ScrollerVideo.svelte';
42
+ export { default as ScrollerVideoForeground } from './components/ScrollerVideo/ScrollerVideoForeground.svelte';
41
43
  export { default as SponsorshipAd } from './components/AdSlot/SponsorshipAd.svelte';
42
44
  export { default as Table } from './components/Table/Table.svelte';
43
45
  export { default as Theme, themes } from './components/Theme/Theme.svelte';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reuters-graphics/graphics-components",
3
- "version": "3.0.12",
3
+ "version": "3.0.13",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "homepage": "https://reuters-graphics.github.io/graphics-components",
@@ -63,6 +63,7 @@
63
63
  "eslint-plugin-storybook": "^0.12.0",
64
64
  "knip": "^5.50.5",
65
65
  "mermaid": "^10.9.3",
66
+ "mp4box": "^0.5.4",
66
67
  "postcss": "^8.5.3",
67
68
  "prettier": "^3.5.3",
68
69
  "prettier-plugin-svelte": "^3.3.3",
@@ -79,6 +80,7 @@
79
80
  "svelte": "^5.28.1",
80
81
  "svelte-check": "^4.1.6",
81
82
  "typescript": "^5.8.3",
83
+ "ua-parser-js": "^2.0.3",
82
84
  "vite": "^6.3.2"
83
85
  },
84
86
  "dependencies": {