Skip to main content

分布式渲染

Remotion Lambda 是我们推荐的解决方案,如果你正在寻找一种方法,将渲染拆分成在不同机器上运行的多个块。

它处理了许多你否则必须自己进行的工程工作:

🌐 It takes care of a lot of engineering work that you would have to undergo yourself otherwise:

  • 协调分布式渲染:定义工作如何拆分,跟踪进度,合并数据块,将其保存到云存储。
  • 通过一个连接流式传输数据块和进度以实现顺畅的进度报告
  • 将 Chrome、Remotion 二进制文件、必要的字体和自定义表情符号字体打包,使它们适合在 Lambda 函数中使用。
  • 在函数调用后清理所有创建的资源,以避免在函数重用时发生内存泄漏。
  • 启用日志,将它们按渲染和块分组,收集并显示错误,对堆栈跟踪进行符号化以辅助 调试 Lambda 渲染
  • 支持多种媒体类型:播放视频、图片、GIF 和音频
  • 为几乎所有 AWS 操作(调用函数、读写对象)实现重试机制
  • 警告你有关错误配置(资源、凭据、权限、有效载荷)
  • 通过将 Lambda 有效负载保存到 S3 来绕过大小限制
  • 在函数调用之间保持浏览器实例打开,以便在下一次调用时准备就绪,但不引用它们,这样你就不会因此被收费。
  • 使用特殊选项无缝地拼接视频和音频而不会出现瑕疵
  • 为 Node、Go、Ruby、Python 和 PHP 实现客户端
  • 实现 Webhooks

你可能不想要一个自定义的分布式渲染器

🌐 You might not want a custom distributed renderer

我们认为 Lambda 在速度、成本、可扩展性和易用性之间达到了最佳平衡。

🌐 We think Lambda is the best balance between speed, cost, scalability and ease of use.

许多用户为他们的 Lambda 设置了过高的内存,从而不必要地导致渲染成本过高。
查看如何优化 Lambda 渲染

🌐 Many users are setting the memory too high for their Lambda cost and are unnecessarily causing their renders to be way too expensive.
See how to optimize a Lambda render.

在开始构建自己的分布式渲染解决方案之前,请考虑你将节省多少钱,并结合实现成本和复杂性进行权衡。

🌐 Before proceeding with building your own distributed rendering solution, consider how much money you are going to save and weigh it against the cost of implementation, given the complexity.

还要考虑 Lambda 函数在渲染完成后立即关闭为你节省了多少开支。

🌐 Also consider how much savings you are getting by Lambda functions shutting down immediately after renders are finished.

实现分布式渲染器

🌐 Implementing a distributed renderer

如果你得出的结论是你仍然需要一个分布式渲染解决方案,这里是如何实现的方法。
Remotion Lambda 也遵循相同的蓝图。

🌐 Should you have come to the conclusion that you still need a distributed rendering solution, here is how to do it.
Remotion Lambda is following the same blueprint as well.

1. 分配工作

🌐 1. Splitting up the work

我们把负责协调渲染的机器称为“主程序”。

🌐 We're calling the machine which orchestrates the render the "main routine".

  • 你必须首先通过调用 selectComposition() 来确定你想要渲染的视频的长度。
  • 如有必要,请考虑 frameRangeeveryNthFrame 选项,以确定你实际渲染了多少帧。
  • 决定每个块你想要渲染多少帧。每个块必须渲染相同数量的帧,除了最后一个块。 如果你使用 aac 音频编解码器,违反此规则可能会导致音频伪影。
  • 将渲染分组成一个范围数组,你将在渲染块时将其用作 frameRange 参数。请记住,帧范围从 0 开始,到 durationInFrames - 1 结束。传递过小或过大的值会导致抛出错误。

2. 调用渲染函数

🌐 2. Invoking render functions

  • 以某种方式调用某个其他机器,该机器会调用 renderMedia()
  • 将你之前计算的这个块的 frameRange 传递给你的渲染调用。
  • 在主程序中传递你获取的值作为composition
  • 将任何其他选项传递给 renderMedia() 调用,但你必须对每个数据块传递相同的选项
  • 确保你不会受到例如 Lambda 所有的 HTTP 请求体有效负载限制的影响。否则,如果输入的 props 或已解析的 props 有效负载过大,你可能无法成功调用渲染。
  • 传递同样的 inputProps 给你传递给 selectComposition() 的。
  • 将此块的 numberOfGifLoops 始终设置为 null
  • 始终将 enforceAudioTrack 选项设置为 true
  • outputLocation 设置为 a
  • compositionStart 设置为正在渲染的帧的整体范围的第一帧。
    • 示例 1:如果你正在渲染完整的组合,每个 renderMedia() 调用都应将 0 设置为 compositionStart
    • 示例 2:如果你的目标是呈现作品的一部分,例如 frameRange: [100, 199],并且你将其分成 4 个部分:[100, 124], [125, 149], [150, 174], [175, 199],每个部分的 compositionStart 值都应为 100
  • 如果你想使用编码器 h264(默认且推荐)进行渲染,或者你想渲染 GIF,请将编码器设置为 h264-ts 而不是 h264
  • 如果你正在渲染:
    • 每个块至少 4 帧 并且
    • 不是使用音频编解码器 aac(这将是默认且推荐的),
  • 然后:
  • 否则:
  • 如果你正在渲染音频,使用 separateAudioTo 选项将音频渲染到单独的文件。
  • 如有需要,处理工件并将其存放在某处,或将其发送到主例程。
  • 参数 concurrencyoffthreadVideoThreads 可以根据需要进行调整,但保持默认值也是可以的。

遵循这些指示并传递正确的值对于在最终拼接音频时正确对齐音频至关重要。

🌐 Following these instructions and passing the right values is crucial for aligning the audio correctly when concatenating it in the end.

将你收到的视频和音频块以及工件传给运行主程序的机器。

🌐 Pass the video and audio chunks, and artifacts that you have received, to the machine running the main routine.

3. 连接这些块

🌐 3. Concatenate the chunks

收集所有音频和视频片段。一旦你收集齐全,就可以使用以下参数调用combineChunks()

🌐 Collect all the audio and video chunks. Once you have all of them, you can call combineChunks() with the following parameters:

  • videoFiles:视频片段的绝对路径数组,必须按顺序排列。
  • audioFiles:一个包含音频片段绝对路径的数组,必须按顺序排列。
  • [outputLocation](/docs/renderer/combine-chunks#outputlocation):存储合并块的绝对文件路径。
  • onProgress:请参阅 combineChunks() 的文档。
  • codec:媒体的最终编解码器。如果你想渲染 h264,你现在应该传递 h264,而不是 h264-ts
  • framesPerChunk:每个分块渲染的帧数。everyNthFrame 选项将会在之后应用,所以不要将其计算在内。
  • fps:必须是你从selectComposition获得的fps的值。
  • preferLossless:如果你也传给了renderMedia(),请在这里也传给它。
  • compositionDurationInFrames:作品的总时长,取自selectComposition()。即使你只渲染其中的一部分,也请传递完整的时长。
  • frameRange:如果你只渲染了作品的一部分,那么你必须在此处也传递该精确的帧范围。请记住,帧范围从 0 开始,到 durationInFrames - 1 结束。传递过小或过大的值将导致抛出错误。

如果你更改了 renderMedia() 调用的默认值,你可能还需要传递额外的参数:

🌐 If you have changed the defaults of a renderMedia() call, you may also have to pass additional parameters:

  • audioCodec:如果你向渲染例程传递了自定义的 audioCodec,那么你也应该在这里传递它。否则,应为 null。如果视频编解码器的默认值本应是 aac,而你向渲染例程传递了 pcm-16,那么你现在可以再次传递 null
  • audioBitrate:你想要的目标比特率。因为你可能已经渲染了
  • numberOfGifLoops:如果你正在渲染 GIF,现在可以设置循环次数(请记住,对于每个块它必须是 null
  • everyNthFrame:如果你把它传递给每个renderMedia()调用,请在这里再次传递。

此外,你可以像使用 renderMedia 一样使用 logLevelmetadatacancelSignalbinariesDirectory 选项。

🌐 Furthermore, you can use the logLevel, metadata, cancelSignal and binariesDirectory options as you would use with renderMedia.

另请参阅

🌐 See also