nodejs-audio-visualizer 2.0.0 → 2.1.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.
@@ -0,0 +1,71 @@
1
+ export interface EncodedBmp {
2
+ shiftPos: number;
3
+ rowBytes: number;
4
+ data: Buffer;
5
+ }
6
+
7
+ export const createBpmEncoder = (imgData: { width: number; height: number; }) => {
8
+ const width = imgData.width;
9
+ const height = imgData.height;
10
+ const extraBytes = width % 4;
11
+ const rgbSize = height * (3 * width + extraBytes);
12
+ const headerInfoSize = 40;
13
+
14
+ /******************header***********************/
15
+ const flag = 'BM';
16
+ const reserved = 0;
17
+ const offset = 54;
18
+ const fileSize = rgbSize + offset;
19
+ const planes = 1;
20
+ const bitPP = 24;
21
+ const compress = 0;
22
+ const hr = 0;
23
+ const vr = 0;
24
+ const colors = 0;
25
+ const importantColors = 0;
26
+
27
+ return (buffer: Buffer): EncodedBmp => {
28
+ const tempBuffer = Buffer.alloc(offset + rgbSize);
29
+
30
+ let pos = 0;
31
+ tempBuffer.write(flag, pos, 2); pos += 2;
32
+ tempBuffer.writeUInt32LE(fileSize, pos); pos += 4;
33
+ tempBuffer.writeUInt32LE(reserved, pos); pos += 4;
34
+ tempBuffer.writeUInt32LE(offset, pos); pos += 4;
35
+
36
+ tempBuffer.writeUInt32LE(headerInfoSize, pos); pos += 4;
37
+ tempBuffer.writeUInt32LE(width, pos); pos += 4;
38
+ tempBuffer.writeInt32LE(-height, pos); pos += 4;
39
+ tempBuffer.writeUInt16LE(planes, pos); pos += 2;
40
+ tempBuffer.writeUInt16LE(bitPP, pos); pos += 2;
41
+ tempBuffer.writeUInt32LE(compress, pos); pos += 4;
42
+ tempBuffer.writeUInt32LE(rgbSize, pos); pos += 4;
43
+ tempBuffer.writeUInt32LE(hr, pos); pos += 4;
44
+ tempBuffer.writeUInt32LE(vr, pos); pos += 4;
45
+ tempBuffer.writeUInt32LE(colors, pos); pos += 4;
46
+ tempBuffer.writeUInt32LE(importantColors, pos); pos += 4;
47
+
48
+ let i = 0;
49
+ const rowBytes = 3 * width + extraBytes;
50
+
51
+ for (let y = 0; y < height; y++) {
52
+ for (let x = 0; x < width; x++) {
53
+ const p = pos + y * rowBytes + x * 3;
54
+ i++; // a
55
+ tempBuffer[p] = buffer[i++]; // b
56
+ tempBuffer[p + 1] = buffer[i++]; // g
57
+ tempBuffer[p + 2] = buffer[i++]; // r
58
+ }
59
+ if (extraBytes > 0) {
60
+ const fillOffset = pos + y * rowBytes + width * 3;
61
+ tempBuffer.fill(0, fillOffset, fillOffset + extraBytes);
62
+ }
63
+ }
64
+
65
+ return {
66
+ shiftPos: pos,
67
+ rowBytes,
68
+ data: tempBuffer
69
+ };
70
+ };
71
+ };
package/src/config.ts CHANGED
@@ -7,7 +7,10 @@ export const defaults = {
7
7
  spectrumHeight: '10%',
8
8
  spectrumX: 'center',
9
9
  spectrumY: 'top',
10
- spectrumRotation: 'down'
10
+ spectrumRotation: 'down',
11
+ spectrumBusesCount: 64,
12
+ spectrumBusMargin: 4,
13
+ spectrumOpacity: '100%',
11
14
  };
12
15
 
13
16
  type RelativePositionValue = 0 | 0.5 | 1;
@@ -43,19 +46,25 @@ export const getOutVideoPath = (config: Config) =>
43
46
  export const getFPS = (config: Config) =>
44
47
  config.outVideo.fps || defaults.fps;
45
48
 
49
+ export const getSpectrumBusesCount = () => defaults.spectrumBusesCount;
50
+
51
+ export const getSpectrumBusMargin = () => defaults.spectrumBusMargin;
52
+
46
53
  const getSpectrumWidth = (config: Config) =>
47
54
  (config.outVideo.spectrum && config.outVideo.spectrum.width) ||
48
55
  defaults.spectrumWidth;
49
56
 
50
57
  const getSpectrumSizeAbsolute = (
51
- spectrumSize: SpectrumSizeValue, backgroundImageSize: number
58
+ spectrumSize: SpectrumSizeValue,
59
+ backgroundImageSize: number,
60
+ fieldName: string
52
61
  ) => {
53
62
  if (typeof spectrumSize === 'number') {
54
63
  return spectrumSize;
55
64
  }
56
65
  const isPercentValue = checkIsPercentValue(spectrumSize);
57
66
  if (!isPercentValue) {
58
- throw new Error();
67
+ throw new Error(getSpectrumSizeValueErrorMessage(fieldName, spectrumSize));
59
68
  }
60
69
  const percentValue = parseInt(spectrumSize) / 100;
61
70
  return backgroundImageSize * percentValue;
@@ -70,11 +79,13 @@ export const getSpectrumWidthAbsolute = (
70
79
  config: Config, backgroundImageWidth: number
71
80
  ) => {
72
81
  const spectrumWidth = getSpectrumWidth(config);
73
- try {
74
- return getSpectrumSizeAbsolute(spectrumWidth, backgroundImageWidth);
75
- } catch {
76
- throw new Error(getSpectrumSizeValueErrorMessage('width', spectrumWidth));
82
+ const spectrumWidthAbsolute = getSpectrumSizeAbsolute(spectrumWidth, backgroundImageWidth, 'width');
83
+ const spectrumWidthWithoutMargin =
84
+ Math.trunc(spectrumWidthAbsolute / defaults.spectrumBusesCount) - defaults.spectrumBusMargin / 2;
85
+ if (spectrumWidthWithoutMargin <= 0) {
86
+ throw new Error(`Spectrum width '${spectrumWidth}' is too small.`);
77
87
  }
88
+ return spectrumWidthAbsolute;
78
89
  };
79
90
 
80
91
  const getSpectrumHeight = (config: Config) =>
@@ -85,11 +96,7 @@ export const getSpectrumHeightAbsolute = (
85
96
  config: Config, backgroundImageHeight: number
86
97
  ) => {
87
98
  const spectrumHeight = getSpectrumHeight(config);
88
- try {
89
- return getSpectrumSizeAbsolute(spectrumHeight, backgroundImageHeight);
90
- } catch {
91
- throw new Error(getSpectrumSizeValueErrorMessage('height', spectrumHeight));
92
- }
99
+ return getSpectrumSizeAbsolute(spectrumHeight, backgroundImageHeight, 'height');
93
100
  };
94
101
 
95
102
  const getSpectrumX = (config: Config) =>
@@ -203,7 +210,26 @@ export const getSpectrumRotation = (config: Config): RotationAliasName => {
203
210
  };
204
211
 
205
212
  export const getSpectrumColor = (config: Config) =>
206
- (config.outVideo.spectrum && config.outVideo.spectrum.color);
213
+ config.outVideo.spectrum && config.outVideo.spectrum.color;
214
+
215
+ export const getSpectrumOpacity = (config: Config) =>
216
+ (config.outVideo.spectrum && config.outVideo.spectrum.opacity) ||
217
+ defaults.spectrumOpacity;
218
+
219
+ export const getSpectrumOpacityParsed = (config: Config) => {
220
+ const spectrumOpacity = getSpectrumOpacity(config);
221
+ if (!checkIsPercentValue(spectrumOpacity)) {
222
+ throw new Error(`Invalid spectrum opacity value: '${spectrumOpacity}'. Use string percent value, for example '80%'.`);
223
+ }
224
+ const percentValue = parseInt(spectrumOpacity);
225
+ if (
226
+ (percentValue < 0) ||
227
+ (percentValue > 100)
228
+ ) {
229
+ throw new Error(`Invalid spectrum opacity value: '${spectrumOpacity}'. Percent values must be in range from '0%' to '100%'.`);
230
+ }
231
+ return percentValue / 100;
232
+ };
207
233
 
208
234
  export const getFfmpeg_cfr = (config: Config) =>
209
235
  config.tweaks && config.tweaks.ffmpeg_cfr;
package/src/image.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { RotationAliasName } from './index';
2
- import { decode, encode, BmpDecoder } from 'bmp-js';
2
+ import { decode, BmpDecoder } from 'bmp-js';
3
+ import { EncodedBmp } from './bpmEncoder';
3
4
  import Jimp from 'jimp';
4
5
 
5
6
  export interface Color {
@@ -18,31 +19,112 @@ interface Size {
18
19
  height: number;
19
20
  }
20
21
 
21
- export const drawRect = (imageDstBuffer: BmpDecoder, position: Position, size: Size, color: Color) => {
22
- for (var currY = position.y; currY < position.y + size.height; currY++) {
23
- for (var currX = position.x; currX < position.x + size.width; currX++) {
24
- var idx = (imageDstBuffer.width * currY + currX) << 2;
22
+ interface MixColorProps {
23
+ color: Color;
24
+ colorOpacity: number;
25
+ backgroundColor: Color;
26
+ }
27
+
28
+ export const mixValues =(value1: number, value1Opacity: number, value2: number) =>
29
+ Math.round(value2 * (1 - value1Opacity) + value1 * value1Opacity);
30
+
31
+ export const mixColors = ({
32
+ color,
33
+ colorOpacity,
34
+ backgroundColor,
35
+ }: MixColorProps): Color => {
36
+ return {
37
+ red: mixValues(color.red, colorOpacity, backgroundColor.red),
38
+ green: mixValues(color.green, colorOpacity, backgroundColor.green),
39
+ blue: mixValues(color.blue, colorOpacity, backgroundColor.blue),
40
+ };
41
+ };
42
+
43
+ const getRectPixelColor = (params: {
44
+ imageDstBuffer: EncodedBmp;
45
+ pixelIndex: number;
46
+ color: Color;
47
+ opacity: number;
48
+ }) => {
49
+ const {
50
+ imageDstBuffer,
51
+ pixelIndex,
52
+ color,
53
+ opacity,
54
+ } = params;
55
+ if (opacity === 1) {
56
+ return color;
57
+ }
58
+
59
+ const backgroundPixelColor: Color = {
60
+ blue: imageDstBuffer.data[pixelIndex],
61
+ green: imageDstBuffer.data[pixelIndex + 1],
62
+ red: imageDstBuffer.data[pixelIndex + 2],
63
+ };
64
+ const resultColor = mixColors({
65
+ color,
66
+ colorOpacity: opacity,
67
+ backgroundColor: backgroundPixelColor,
68
+ });
69
+ return resultColor;
70
+ };
25
71
 
26
- imageDstBuffer.data[idx + 1] = color.blue;
27
- imageDstBuffer.data[idx + 2] = color.green;
28
- imageDstBuffer.data[idx + 3] = color.red;
72
+ interface DrawRectPorps {
73
+ imageDstBuffer: EncodedBmp;
74
+ position: Position;
75
+ size: Size;
76
+ color: Color;
77
+ opacity: number;
78
+ }
79
+
80
+ export const drawRect = ({
81
+ imageDstBuffer,
82
+ position,
83
+ size,
84
+ color,
85
+ opacity,
86
+ }: DrawRectPorps) => {
87
+ for (let currY = position.y; currY < position.y + size.height; currY++) {
88
+ for (let currX = position.x; currX < position.x + size.width; currX++) {
89
+ const pixelIndex = imageDstBuffer.shiftPos + currY * imageDstBuffer.rowBytes + currX * 3;
90
+ const pixelColor = getRectPixelColor({
91
+ imageDstBuffer,
92
+ pixelIndex,
93
+ color,
94
+ opacity,
95
+ });
96
+ imageDstBuffer.data[pixelIndex] = pixelColor.blue;
97
+ imageDstBuffer.data[pixelIndex + 1] = pixelColor.green;
98
+ imageDstBuffer.data[pixelIndex + 2] = pixelColor.red;
29
99
  }
30
100
  }
31
101
  };
32
102
 
33
- const drawSpectrum = (
34
- imageDstBuffer: BmpDecoder,
35
- spectrum: number[],
36
- size: Size,
37
- position: Position,
38
- rotation: RotationAliasName,
39
- color: Color
40
- ) => {
103
+ interface DrawSpectrumProps {
104
+ imageDstBuffer: EncodedBmp;
105
+ spectrum: number[];
106
+ size: Size;
107
+ position: Position;
108
+ rotation: RotationAliasName;
109
+ margin: number;
110
+ color: Color;
111
+ opacity: number;
112
+ }
113
+
114
+ const drawSpectrum = ({
115
+ imageDstBuffer,
116
+ spectrum,
117
+ size,
118
+ position,
119
+ rotation,
120
+ margin,
121
+ color,
122
+ opacity,
123
+ }: DrawSpectrumProps) => {
41
124
  const busWidth = Math.trunc(size.width / spectrum.length);
42
125
  const left = Math.trunc(position.x);
43
126
  const top = Math.trunc(position.y);
44
127
  const height = Math.trunc(size.height);
45
- const margin = 4;
46
128
 
47
129
  for (let spectrumX = 0; spectrumX < spectrum.length; spectrumX++) {
48
130
  const spectrumValue = spectrum[spectrumX];
@@ -53,30 +135,71 @@ const drawSpectrum = (
53
135
  const rectX = left + busWidth * spectrumX;
54
136
  const rectHeight = Math.trunc(height * spectrumValue);
55
137
  const rectY = (rotation === 'up') ? top + height - rectHeight : top;
56
- drawRect(imageDstBuffer, { x: rectX + margin, y: rectY }, { width: busWidth - margin / 2, height: rectHeight }, color);
138
+ drawRect({
139
+ imageDstBuffer,
140
+ position: { x: rectX + margin, y: rectY },
141
+ size: { width: busWidth - margin / 2, height: rectHeight },
142
+ color,
143
+ opacity,
144
+ });
57
145
  }
58
146
  };
59
147
 
60
- export const createVisualizerFrame = (backgroundImageBuffer: BmpDecoder, spectrum: number[], size: Size, position: Position, rotation: RotationAliasName, color: Color | string) => {
61
- const image = Object.assign({}, backgroundImageBuffer);
62
- image.data = Buffer.from(image.data);
148
+ interface CreateVisualizerFrameProps {
149
+ backgroundImageBuffer: EncodedBmp;
150
+ spectrum: number[];
151
+ size: Size;
152
+ position: Position;
153
+ rotation: RotationAliasName;
154
+ margin: number;
155
+ color: Color | string;
156
+ opacity: number;
157
+ }
158
+
159
+ export const createVisualizerFrame = ({
160
+ backgroundImageBuffer,
161
+ spectrum,
162
+ size,
163
+ position,
164
+ rotation,
165
+ margin,
166
+ color,
167
+ opacity,
168
+ }: CreateVisualizerFrameProps) => {
169
+ const imageDstBuffer = Object.assign({}, backgroundImageBuffer);
170
+ imageDstBuffer.data = Buffer.from(backgroundImageBuffer.data);
171
+
63
172
  const rgbSpectrumColor = (typeof color === 'string') ? hexToRgb(color) : color;
64
- drawSpectrum(image, spectrum, size, position, rotation, rgbSpectrumColor);
65
- return image;
173
+ drawSpectrum({
174
+ imageDstBuffer,
175
+ spectrum,
176
+ size,
177
+ position,
178
+ rotation,
179
+ margin,
180
+ color: rgbSpectrumColor,
181
+ opacity,
182
+ });
183
+ return imageDstBuffer;
66
184
  };
67
185
 
68
186
  export const convertToBmp = async (filePath: string) =>
69
- new Promise<Buffer>(async resolve => {
70
- const image = await Jimp.read(filePath);
71
- image.getBuffer("image/bmp", (err, value) => {
72
- resolve(value);
73
- });
187
+ new Promise<Buffer>(async (resolve, reject) => {
188
+ try {
189
+ const image = await Jimp.read(filePath);
190
+ image.getBuffer("image/bmp", (err, value) => {
191
+ if (err) {
192
+ reject(err);
193
+ }
194
+ resolve(value);
195
+ });
196
+ } catch {
197
+ throw new Error(`Cannot procces image file from path: ${filePath}`);
198
+ }
74
199
  });
75
200
 
76
201
  export const parseImage = (buffer: Buffer) => decode(buffer);
77
202
 
78
- export const createImageBuffer = (image: BmpDecoder) => encode(image).data;
79
-
80
203
  export const getImageColor = (image: BmpDecoder): Color => {
81
204
  let blueSum = 0;
82
205
  let greenSum = 0;
package/src/index.ts CHANGED
@@ -3,11 +3,14 @@ import {
3
3
  getBackgroundImagePath,
4
4
  getOutVideoPath,
5
5
  getFPS,
6
+ getSpectrumBusesCount,
7
+ getSpectrumBusMargin,
6
8
  getSpectrumWidthAbsolute,
7
9
  getSpectrumHeightAbsolute,
8
10
  getSpectrumXAbsolute,
9
11
  getSpectrumYAbsolute,
10
12
  getSpectrumColor,
13
+ getSpectrumOpacityParsed,
11
14
  getFfmpeg_cfr,
12
15
  getFfmpeg_preset,
13
16
  getFrame_processing_delay,
@@ -15,8 +18,9 @@ import {
15
18
  getSpectrumRotation
16
19
  } from './config';
17
20
  import { createAudioBuffer, bufferToUInt8, createSpectrumsProcessor } from './audio';
18
- import { parseImage, createVisualizerFrame, createImageBuffer, getImageColor, invertColor, Color, convertToBmp } from './image';
21
+ import { parseImage, createVisualizerFrame, getImageColor, invertColor, Color, convertToBmp } from './image';
19
22
  import { spawnFfmpegVideoWriter, getProgress, calculateProgress, waitDrain } from './video';
23
+ import { createBpmEncoder } from './bpmEncoder';
20
24
 
21
25
  export const PCM_FORMAT = {
22
26
  bit: 8,
@@ -42,6 +46,7 @@ export interface Config {
42
46
  y?: number | PositionAliasName;
43
47
  rotation?: RotationAliasName;
44
48
  color?: Color | string;
49
+ opacity?: string;
45
50
  }
46
51
  };
47
52
  tweaks?: {
@@ -81,6 +86,8 @@ export const renderAudioVisualizer = (config: Config, onProgress?: (progress: nu
81
86
  throw new Error('ffmpeg didn\'t show audio sample rate');
82
87
  }
83
88
 
89
+ const spectrumBusesCount = getSpectrumBusesCount();
90
+ const spectrumBusMargin = getSpectrumBusMargin();
84
91
  const FPS = getFPS(config);
85
92
  const spectrumWidth =
86
93
  getSpectrumWidthAbsolute(config, backgroundImage.width);
@@ -95,10 +102,10 @@ export const renderAudioVisualizer = (config: Config, onProgress?: (progress: nu
95
102
  const spectrumColor =
96
103
  getSpectrumColor(config) ||
97
104
  invertColor(getImageColor(backgroundImage));
105
+ const spectrumOpacity = getSpectrumOpacityParsed(config);
98
106
  const ffmpeg_cfr = getFfmpeg_cfr(config);
99
107
  const ffmpeg_preset = getFfmpeg_preset(config);
100
108
  const frame_processing_delay = getFrame_processing_delay(config);
101
- const spectrumBusesCount = 64;
102
109
 
103
110
  const audioDuration = audioBuffer.length / sampleRate;
104
111
  const framesCount = Math.trunc(audioDuration * FPS);
@@ -114,21 +121,24 @@ export const renderAudioVisualizer = (config: Config, onProgress?: (progress: nu
114
121
  });
115
122
  ffmpegVideoWriter.on('exit', (code: number) => resolve(code));
116
123
 
124
+ const bpmEncoder = createBpmEncoder({ width: backgroundImage.width, height: backgroundImage.height });
125
+ const backgroundImageBuffer = bpmEncoder(backgroundImage.data);
117
126
  const processSpectrum = createSpectrumsProcessor(spectrumBusesCount);
118
127
  for (let i = 0; i < framesCount; i++) {
119
128
  const audioDataParser = () =>
120
129
  PCM_FORMAT.parseFunction(audioBuffer, i * audioDataStep, i * audioDataStep + audioDataStep);
121
130
  const spectrum = processSpectrum(i, audioDataParser);
122
- const frameImage = createVisualizerFrame(
123
- backgroundImage,
131
+ const frameImage = createVisualizerFrame({
132
+ backgroundImageBuffer,
124
133
  spectrum,
125
- { width: spectrumWidth, height: spectrumHeight },
126
- { x: spectrumX, y: spectrumY },
127
- spectrumRotation,
128
- spectrumColor
129
- );
130
- const frameImageBuffer = createImageBuffer(frameImage);
131
- const isFrameProcessed = ffmpegVideoWriter.stdin.write(frameImageBuffer);
134
+ size: { width: spectrumWidth, height: spectrumHeight },
135
+ position: { x: spectrumX, y: spectrumY },
136
+ rotation: spectrumRotation,
137
+ margin: spectrumBusMargin,
138
+ color: spectrumColor,
139
+ opacity: spectrumOpacity,
140
+ });
141
+ const isFrameProcessed = ffmpegVideoWriter.stdin.write(frameImage.data);
132
142
  if (!isFrameProcessed) {
133
143
  await waitDrain(ffmpegVideoWriter.stdin);
134
144
  }
@@ -12,6 +12,8 @@ import {
12
12
  getSpectrumXAbsolute,
13
13
  getSpectrumYAbsolute,
14
14
  getSpectrumColor,
15
+ getSpectrumOpacity,
16
+ getSpectrumOpacityParsed,
15
17
  getFfmpeg_cfr,
16
18
  getFfmpeg_preset,
17
19
  getFrame_processing_delay,
@@ -94,6 +96,24 @@ describe('config', function() {
94
96
  backgroundImageWidth
95
97
  );
96
98
  expect(result4).to.throw('Invalid spectrum width value: \'69\'. Use number value or percent value in string, for example: \'30%\'.');
99
+
100
+ const result5 = getSpectrumWidthAbsolute.bind(
101
+ undefined,
102
+ {
103
+ outVideo: { spectrum: { width: 1 }}
104
+ } as Config,
105
+ backgroundImageWidth
106
+ );
107
+ expect(result5).to.throw('Spectrum width \'1\' is too small.');
108
+
109
+ const result6 = getSpectrumWidthAbsolute.bind(
110
+ undefined,
111
+ {
112
+ outVideo: { spectrum: { width: '1%' }}
113
+ } as Config,
114
+ backgroundImageWidth
115
+ );
116
+ expect(result6).to.throw('Spectrum width \'1%\' is too small.');
97
117
  });
98
118
 
99
119
  it('getSpectrumHeightAbsolute', function() {
@@ -267,6 +287,46 @@ describe('config', function() {
267
287
  expect(result2).equal('#cccc99');
268
288
  });
269
289
 
290
+ it('getSpectrumOpacity', function() {
291
+ const result1 = getSpectrumOpacity({
292
+ outVideo: {}
293
+ } as Config);
294
+ expect(result1).equal(defaults.spectrumOpacity);
295
+
296
+ const result2 = getSpectrumOpacity({
297
+ outVideo: { spectrum: { opacity: '69%' } }
298
+ } as Config);
299
+ expect(result2).equal('69%');
300
+ });
301
+
302
+ it('getSpectrumOpacityParsed', function() {
303
+ const result1 = getSpectrumOpacityParsed({
304
+ outVideo: {}
305
+ } as Config);
306
+ expect(result1).equal(parseInt(defaults.spectrumOpacity) / 100);
307
+
308
+ const result2 = getSpectrumOpacityParsed({
309
+ outVideo: { spectrum: { opacity: '69%' } }
310
+ } as Config);
311
+ expect(result2).equal(0.69);
312
+
313
+ const result3 = getSpectrumOpacityParsed.bind(
314
+ undefined,
315
+ {
316
+ outVideo: { spectrum: { opacity: 'fake value' } }
317
+ } as Config
318
+ );
319
+ expect(result3).to.throw('Invalid spectrum opacity value: \'fake value\'. Use string percent value, for example \'80%\'.');
320
+
321
+ const result4 = getSpectrumOpacityParsed.bind(
322
+ undefined,
323
+ {
324
+ outVideo: { spectrum: { opacity: '103%' } }
325
+ } as Config
326
+ );
327
+ expect(result4).to.throw('Invalid spectrum opacity value: \'103%\'. Percent values must be in range from \'0%\' to \'100%\'.');
328
+ });
329
+
270
330
  it('getFfmpeg_cfr', function() {
271
331
  const result1 = getFfmpeg_cfr({
272
332
  tweaks: {}