@remotion/renderer 3.2.12 → 3.2.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.
@@ -29,6 +29,7 @@ const downloadFile = ({ onProgress, url, to: toFn, }) => {
29
29
  return resolve({ sizeInBytes: downloaded, to });
30
30
  });
31
31
  writeStream.on('error', (err) => reject(err));
32
+ res.on('error', (err) => reject(err));
32
33
  res.pipe(writeStream).on('error', (err) => reject(err));
33
34
  res.on('data', (d) => {
34
35
  downloaded += d.length;
@@ -4,4 +4,5 @@ declare type MediaSupport = {
4
4
  audio: boolean;
5
5
  };
6
6
  export declare const codecSupportsMedia: (codec: Codec) => MediaSupport;
7
+ export declare const codecSupportsCrf: (codec: Codec) => boolean | "" | null;
7
8
  export {};
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.codecSupportsMedia = void 0;
3
+ exports.codecSupportsCrf = exports.codecSupportsMedia = void 0;
4
+ const get_codec_name_1 = require("./get-codec-name");
4
5
  const support = {
5
6
  'h264-mkv': {
6
7
  audio: true,
@@ -47,3 +48,9 @@ const codecSupportsMedia = (codec) => {
47
48
  return support[codec];
48
49
  };
49
50
  exports.codecSupportsMedia = codecSupportsMedia;
51
+ const codecSupportsCrf = (codec) => {
52
+ const encoderName = (0, get_codec_name_1.getCodecName)(codec);
53
+ const supportsCrf = encoderName && codec !== 'prores';
54
+ return supportsCrf;
55
+ };
56
+ exports.codecSupportsCrf = codecSupportsCrf;
@@ -1,8 +1,5 @@
1
1
  import type { DownloadMap } from './assets/download-map';
2
2
  export declare const createFfmpegComplexFilter: (filters: number, downloadMap: DownloadMap) => Promise<{
3
- complexFilterFlag: [
4
- string,
5
- string
6
- ] | null;
3
+ complexFilterFlag: [string, string] | null;
7
4
  cleanup: () => void;
8
5
  }>;
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  import type { OffthreadVideoImageFormat } from 'remotion';
2
3
  import type { DownloadMap } from './assets/download-map';
3
4
  import type { FfmpegExecutable } from './ffmpeg-executable';
@@ -32,9 +32,9 @@ const determineResizeParams = (needsResize) => {
32
32
  };
33
33
  // Uses no seeking, therefore the whole video has to be decoded. This is a last resort and should only happen
34
34
  // if the video is corrupted
35
- const getFrameOfVideoSlow = async ({ src, timestamp, ffmpegExecutable, imageFormat, specialVCodecForTransparency, needsResize, }) => {
36
- console.warn(`\nUsing a slow method to extract the frame at ${timestamp}ms of ${src}. See https://remotion.dev/docs/slow-method-to-extract-frame for advice`);
37
- const actualOffset = `-${timestamp * 1000}ms`;
35
+ const getFrameOfVideoSlow = async ({ src, duration, ffmpegExecutable, imageFormat, specialVCodecForTransparency, needsResize, offset, fps, }) => {
36
+ console.warn(`\nUsing a slow method to extract the frame at ${duration}ms of ${src}. See https://remotion.dev/docs/slow-method-to-extract-frame for advice`);
37
+ const actualOffset = `-${duration * 1000 - offset}ms`;
38
38
  const command = [
39
39
  '-itsoffset',
40
40
  actualOffset,
@@ -72,7 +72,20 @@ const getFrameOfVideoSlow = async ({ src, timestamp, ffmpegExecutable, imageForm
72
72
  const [stdErr, stdoutBuffer] = await Promise.all([stdErrString, stdoutChunk]);
73
73
  const isEmpty = stdErr.includes('Output file is empty');
74
74
  if (isEmpty) {
75
- throw new Error(`Could not get last frame of ${src}. Tried to seek to the end using the command "ffmpeg ${command.join(' ')}" but got no frame. Most likely this video is corrupted.`);
75
+ if (offset > 70) {
76
+ throw new Error(`Could not get last frame of ${src}. Tried to seek to the end using the command "ffmpeg ${command.join(' ')}" but got no frame. Most likely this video is corrupted.`);
77
+ }
78
+ return getFrameOfVideoSlow({
79
+ ffmpegExecutable,
80
+ duration,
81
+ // Decrement in 10ms increments, or 1 frame (e.g. fps = 25 --> 40ms)
82
+ offset: offset + (fps === null ? 10 : 1000 / fps),
83
+ src,
84
+ imageFormat,
85
+ specialVCodecForTransparency,
86
+ needsResize,
87
+ fps,
88
+ });
76
89
  }
77
90
  return stdoutBuffer;
78
91
  };
@@ -88,12 +101,14 @@ const getLastFrameOfVideoFastUnlimited = async (options) => {
88
101
  }
89
102
  if (options.specialVCodecForTransparency === 'vp8' || offset > 40) {
90
103
  const last = await getFrameOfVideoSlow({
91
- timestamp: duration,
104
+ duration,
92
105
  ffmpegExecutable,
93
106
  src,
94
107
  imageFormat: options.imageFormat,
95
108
  specialVCodecForTransparency: options.specialVCodecForTransparency,
96
109
  needsResize: options.needsResize,
110
+ offset: offset - 1000 / (fps === null ? 10 : fps),
111
+ fps,
97
112
  });
98
113
  return last;
99
114
  }
@@ -143,7 +158,7 @@ const getLastFrameOfVideoFastUnlimited = async (options) => {
143
158
  const unlimited = await getLastFrameOfVideoFastUnlimited({
144
159
  ffmpegExecutable,
145
160
  // Decrement in 10ms increments, or 1 frame (e.g. fps = 25 --> 40ms)
146
- offset: offset + 1000 / (fps === null ? 10 : fps),
161
+ offset: offset + (fps === null ? 10 : 1000 / fps),
147
162
  src,
148
163
  ffprobeExecutable,
149
164
  imageFormat: options.imageFormat,
@@ -167,13 +182,16 @@ const extractFrameFromVideoFn = async ({ time, ffmpegExecutable, ffprobeExecutab
167
182
  const src = await (0, ensure_presentation_timestamp_1.ensurePresentationTimestamps)(downloadMap, options.src);
168
183
  const { specialVcodec, needsResize } = await (0, get_video_info_1.getVideoInfo)(downloadMap, src, ffprobeExecutable);
169
184
  if (specialVcodec === 'vp8') {
185
+ const { fps } = await (0, get_video_stream_duration_1.getVideoStreamDuration)(downloadMap, src, ffprobeExecutable);
170
186
  return getFrameOfVideoSlow({
171
187
  ffmpegExecutable,
172
188
  imageFormat,
173
189
  specialVCodecForTransparency: specialVcodec,
174
190
  src,
175
- timestamp: time,
191
+ duration: time,
176
192
  needsResize,
193
+ offset: 0,
194
+ fps,
177
195
  });
178
196
  }
179
197
  if ((0, is_beyond_last_frame_1.isBeyondLastFrame)(downloadMap, src, time)) {
@@ -243,6 +261,11 @@ const extractFrameFromVideoFn = async ({ time, ffmpegExecutable, ffprobeExecutab
243
261
  });
244
262
  return last;
245
263
  }
264
+ if (stdOut.length === 0) {
265
+ console.log('FFMPEG Logs:');
266
+ console.log(stderrStr);
267
+ throw new Error("Couldn't extract frame from video - FFMPEG did not return any data. Check logs to see more information");
268
+ }
246
269
  return stdOut;
247
270
  };
248
271
  const extractFrameFromVideo = async (options) => {
@@ -1,2 +1,2 @@
1
1
  import type { Codec } from './codec';
2
- export declare const getFileExtensionFromCodec: (codec: Codec, type: 'chunk' | 'final') => "mp3" | "aac" | "wav" | "gif" | "mp4" | "mkv" | "mov" | "webm";
2
+ export declare const getFileExtensionFromCodec: (codec: Codec, type: 'chunk' | 'final') => "mp3" | "aac" | "wav" | "gif" | "webm" | "mp4" | "mov" | "mkv";
@@ -1 +1 @@
1
- export declare const guessExtensionForVideo: (src: string) => Promise<"mp3" | "wav" | "mp4" | "webm">;
1
+ export declare const guessExtensionForVideo: (src: string) => Promise<"mp3" | "wav" | "webm" | "mp4">;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  import execa from 'execa';
2
3
  import { SymbolicateableError } from './error-handling/symbolicateable-error';
3
4
  import { mimeContentType, mimeLookup } from './mime-types';
@@ -67,7 +68,7 @@ export declare const RenderInternals: {
67
68
  task: Promise<Buffer | null>;
68
69
  getLogs: () => string;
69
70
  }>;
70
- getFileExtensionFromCodec: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif", type: "chunk" | "final") => "mp3" | "aac" | "wav" | "gif" | "mp4" | "mkv" | "mov" | "webm";
71
+ getFileExtensionFromCodec: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif", type: "chunk" | "final") => "mp3" | "aac" | "wav" | "gif" | "webm" | "mp4" | "mov" | "mkv";
71
72
  tmpDir: (str: string) => string;
72
73
  deleteDirectory: (directory: string) => Promise<void>;
73
74
  isServeUrl: (potentialUrl: string) => boolean;
@@ -122,8 +123,8 @@ export declare const RenderInternals: {
122
123
  validPixelFormats: readonly ["yuv420p", "yuva420p", "yuv422p", "yuv444p", "yuv420p10le", "yuv422p10le", "yuv444p10le", "yuva444p10le"];
123
124
  DEFAULT_BROWSER: import("./browser").Browser;
124
125
  validateFrameRange: (frameRange: import("./frame-range").FrameRange | null) => void;
125
- DEFAULT_OPENGL_RENDERER: "swangle" | "angle" | "egl" | "swiftshader" | null;
126
- validateOpenGlRenderer: (option: "swangle" | "angle" | "egl" | "swiftshader" | null) => "swangle" | "angle" | "egl" | "swiftshader" | null;
126
+ DEFAULT_OPENGL_RENDERER: "angle" | "swangle" | "egl" | "swiftshader" | null;
127
+ validateOpenGlRenderer: (option: "angle" | "swangle" | "egl" | "swiftshader" | null) => "angle" | "swangle" | "egl" | "swiftshader" | null;
127
128
  getDefaultCrfForCodec: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif") => number;
128
129
  validateSelectedCrfAndCodecCombination: (crf: unknown, codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif") => void;
129
130
  validImageFormats: readonly ["png", "jpeg", "none"];
@@ -135,12 +136,12 @@ export declare const RenderInternals: {
135
136
  DEFAULT_TIMEOUT: number;
136
137
  getValidCrfRanges: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif") => [number, number];
137
138
  validateSelectedPixelFormatAndCodecCombination: (pixelFormat: "yuv420p" | "yuva420p" | "yuv422p" | "yuv444p" | "yuv420p10le" | "yuv422p10le" | "yuv444p10le" | "yuva444p10le", codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif") => void;
138
- validateSelectedCodecAndProResCombination: (actualCodec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif", actualProResProfile: "4444-xq" | "4444" | "hq" | "standard" | "light" | "proxy" | undefined) => void;
139
- validateSelectedPixelFormatAndImageFormatCombination: (pixelFormat: "yuv420p" | "yuva420p" | "yuv422p" | "yuv444p" | "yuv420p10le" | "yuv422p10le" | "yuv444p10le" | "yuva444p10le", imageFormat: "png" | "jpeg" | "none") => "none" | "valid";
139
+ validateSelectedCodecAndProResCombination: (actualCodec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif", actualProResProfile: "proxy" | "4444-xq" | "4444" | "hq" | "standard" | "light" | undefined) => void;
140
+ validateSelectedPixelFormatAndImageFormatCombination: (pixelFormat: "yuv420p" | "yuva420p" | "yuv422p" | "yuv444p" | "yuv420p10le" | "yuv422p10le" | "yuv444p10le" | "yuva444p10le", imageFormat: "jpeg" | "png" | "none") => "none" | "valid";
140
141
  DEFAULT_CODEC: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif";
141
142
  isAudioCodec: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif" | undefined) => boolean;
142
143
  logLevels: readonly ["verbose", "info", "warn", "error"];
143
- isEqualOrBelowLogLevel: (currentLevel: "verbose" | "info" | "warn" | "error", level: "verbose" | "info" | "warn" | "error") => boolean;
144
+ isEqualOrBelowLogLevel: (currentLevel: "error" | "verbose" | "info" | "warn", level: "error" | "verbose" | "info" | "warn") => boolean;
144
145
  isValidLogLevel: (level: string) => boolean;
145
146
  validateEveryNthFrame: (everyNthFrame: unknown) => void;
146
147
  perf: typeof perf;
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  import type { OffthreadVideoImageFormat } from 'remotion';
2
3
  import type { DownloadMap, SpecialVCodecForTransparency } from './assets/download-map';
3
4
  import type { FfmpegExecutable } from './ffmpeg-executable';
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  import type { Page } from './browser/BrowserPage';
2
3
  import type { ImageFormat } from './image-format';
3
4
  export declare const provideScreenshot: ({ page, imageFormat, options, quality, }: {
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  import type { Page } from './browser/BrowserPage';
2
3
  import type { ScreenshotOptions } from './browser/ScreenshotOptions';
3
4
  export declare const screenshot: (page: Page, options: ScreenshotOptions) => Promise<Buffer | string | void>;
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  import type { SmallTCompMetadata } from 'remotion';
2
3
  import type { RenderMediaOnDownload } from './assets/download-and-map-assets-to-file';
3
4
  import type { DownloadMap } from './assets/download-map';
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  import type { Page } from './browser/BrowserPage';
2
3
  import type { ImageFormat } from './image-format';
3
4
  export declare const screenshotDOMElement: ({ page, imageFormat, quality, opts, }: {
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  import type { Page } from './browser/BrowserPage';
2
3
  import type { ScreenshotOptions } from './browser/ScreenshotOptions';
3
4
  import type { StillImageFormat } from './image-format';
@@ -96,7 +96,7 @@ const spawnFfmpeg = async (options) => {
96
96
  const audioCodecName = (0, get_audio_codec_name_1.getAudioCodecName)(codec);
97
97
  const proResProfileName = (0, get_prores_profile_name_1.getProResProfileName)(codec, options.proResProfile);
98
98
  const mediaSupport = (0, codec_supports_media_1.codecSupportsMedia)(codec);
99
- const supportsCrf = encoderName && codec !== 'prores';
99
+ const supportsCrf = (0, codec_supports_media_1.codecSupportsCrf)(codec);
100
100
  const tempFile = options.outputLocation
101
101
  ? null
102
102
  : path_1.default.join(options.assetsInfo.downloadMap.stitchFrames, `out.${(0, get_extension_from_codec_1.getFileExtensionFromCodec)(codec, 'final')}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remotion/renderer",
3
- "version": "3.2.12",
3
+ "version": "3.2.13",
4
4
  "description": "Renderer for Remotion",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -22,7 +22,7 @@
22
22
  "dependencies": {
23
23
  "execa": "5.1.1",
24
24
  "extract-zip": "2.0.1",
25
- "remotion": "3.2.12",
25
+ "remotion": "3.2.13",
26
26
  "source-map": "^0.8.0-beta.0",
27
27
  "ws": "8.7.0"
28
28
  },
@@ -57,5 +57,5 @@
57
57
  "publishConfig": {
58
58
  "access": "public"
59
59
  },
60
- "gitHead": "72c9a148cafdd4468fff41bbc7ca70a1776fb15e"
60
+ "gitHead": "989d20fc0ace36685c71396847e1b7b7a06d00d7"
61
61
  }