创建视频非常耗时。一个制作精良的 5 分钟视频可能需要花费数小时来计划、录制和编辑——这还是在开始讨论如何使该视频与网站上所有其他视频保持一致之前。
当我们承担Jamstack Explorers 项目(一个面向 Web 开发人员的视频驱动型教育资源)时,我们希望找到质量和交付之间的正确平衡:在我们的视频制作过程中,我们可以自动化哪些内容以减少创建视频内容所需的时间和步骤,同时又不牺牲质量?
在Cloudinary的帮助下,我们能够在所有视频内容中提供一致的品牌推广方式,而无需为创建视频的人员添加大量额外的编辑任务。此外,作为一个额外的好处,如果我们将来更新我们的品牌,我们可以立即更新整个网站上的所有视频品牌——无需进行视频编辑!
“视频品牌推广”是什么意思?
为了使 Explorers 网站上的每个视频都感觉像是整体的一部分,我们在每个视频中都包含了一些常见的部分
- 标题场景
- 一个简短的开头缓冲区(视频剪辑),显示 Jamstack Explorers 品牌
- 一个简短的结尾缓冲区,要么倒计时到下一个视频,要么在这是任务中的最后一个视频时显示“任务完成”
跳到结尾:以下是品牌视频的外观
为了显示添加品牌的影響,以下是 Jamstack Explorers 中的一个视频,没有任何品牌
这段视频(以及Ben Hong 的这段 Vue 任务)确实非常出色!但是,它开始和结束都有些突然,而且我们不知道这段视频属于哪个位置。
我们与 Adam Hald 合作创建了品牌视频资产,以帮助每个视频获得归属感。查看应用了所有 Explorers 品牌的相同视频
我们获得了相同的内容,但现在我们添加了一些额外的魅力,使它感觉像是更大故事的一部分。
在本文中,我们将逐步介绍如何使用 Cloudinary 自动自定义每个视频。
Cloudinary 如何实现这一点?
Cloudinary 是一个基于云的资产交付网络,它为我们提供了一个强大的基于 URL 的 API 来操作和转换媒体。它支持各种资产类型,但它真正闪光的地方在于图像和视频。
要使用 Cloudinary,您需要创建一个免费帐户,然后上传您的资产。然后,此资产将在 Cloudinary URL 中可用
https://res.cloudinary.com/netlify/image/upload/v1605632851/explorers/avatar.jpg
^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
| | |
V V V
cloud (account) name version (optional) file name
此 URL 指向原始图像,并可用于<img />
标签和其他标记中。

动态调整文件格式和质量以减小文件大小
如果我们在网站上使用此图像并希望提高网站性能,我们可能会决定通过使用 WebP、AVIF 等下一代格式来减小此图像的大小。这些新格式要小得多,但并非所有浏览器都支持,这通常意味着使用工具生成此图像的不同格式的多个版本,然后使用<picture>
元素或其他专门的标记来提供现代选项,并为旧版浏览器提供 JPG 备用。
使用 Cloudinary,我们只需向 URL 添加一个转换即可
https://res.cloudinary.com/netlify/image/upload/q_auto,f_auto/v1605632851/explorers/avatar.jpg
^^^^^^^^^^^^
|
V
automatic quality & format transformations
我们在浏览器中看到的内容在视觉上是相同的

通过将文件格式和质量设置设置为自动 (f_auto,q_auto
),Cloudinary 能够检测客户端支持哪些格式,并在合理的质量级别下提供最高效的格式。例如,在 Chrome 中,此图像从 97.6kB JPG 转换为 15.4kB WebP,而我们只需向 URL 添加几件事!
我们可以通过多种不同的方式转换我们的图像!
我们可以通过其他转换进一步操作,包括调整大小 (w_150
表示“调整为 150px 宽”) 和颜色效果 (e_grayscale
表示“应用灰度效果”)
https://res.cloudinary.com/netlify/image/upload/q_auto,f_auto,w_150,e_grayscale/v1605632851/explorers/avatar.jpg

这只是其中一小部分——请务必查看 Cloudinary 文档以获取更多示例!
有一个 Node SDK 可以使代码更易于阅读
对于更高级的转换(例如我们将要讨论的转换),手动编写 URL 可能会变得难以阅读。我们最终使用了Cloudinary Node SDK来使我们能够添加注释并解释每个转换的作用,这在我们维护和发展平台时非常有帮助。
要安装它,请从您的控制台获取您的 Cloudinary API 密钥和密钥,然后使用 npm 安装 SDK
# create a new directory
mkdir cloudinary-video
# move into the new directory
cd cloudinary-video/
# initialize a new Node project
npm init -y
# install the Cloudinary Node SDK
npm install cloudinary
接下来,创建一个名为index.js
的新文件,并使用您的cloud_name
和 API 凭据初始化 SDK
const cloudinary = require('cloudinary').v2;
// TODO replace these values with your own Cloudinary credentials
cloudinary.config({
cloud_name: 'your_cloud_name',
api_key: 'your_api_key',
api_secret: 'your_api_secret',
});
不要将您的 API 凭据提交到 GitHub 或在任何地方共享。使用环境变量来确保它们的安全!如果您不熟悉环境变量,Colby Fayock 撰写了一篇关于如何使用环境变量的优秀入门文章。
接下来,我们可以创建与之前相同的转换,使用更易于阅读的配置设置
cloudinary.uploader
// the first argument should be the public ID (including folders!) of the
// image we want to transform
.explicit('explorers/avatar', {
// these two properties match the beginning of the URL:
// https://res.cloudinary.com/netlify/image/upload/...
// ^^^^^^^^^^^^
resource_type: 'image',
type: 'upload',
// "eager" means we want to run these transformations ahead of time to avoid
// a slow first load time
eager: [
{
fetch_format: 'auto',
quality: 'auto',
width: 150,
effect: 'grayscale',
},
],
// allow this transformed image to be cached to avoid re-running the same
// transformations over and over again
overwrite: false,
})
.then((result) => {
console.log(result);
});
让我们通过在终端中键入node index.js
来运行此代码。输出将如下所示
{
asset_id: 'fca4abba96ffdf70ef89498aa340ae4e',
public_id: 'explorers/avatar',
version: 1605632851,
version_id: 'b8a923931af20404e89d03852ff1bff1',
signature: 'e7201c9ab36cb5b6a0545cee4f5f8ee27fb7f99f',
width: 300,
height: 300,
format: 'jpg',
resource_type: 'image',
created_at: '2020-11-17T17:07:31Z',
bytes: 97633,
type: 'upload',
url: 'http://res.cloudinary.com/netlify/image/upload/v1605632851/explorers/avatar.jpg',
secure_url: 'https://res.cloudinary.com/netlify/image/upload/v1605632851/explorers/avatar.jpg',
access_mode: 'public',
eager: [
{
transformation: 'e_grayscale,f_auto,q_auto,w_150',
width: 150,
height: 150,
bytes: 6192,
format: 'jpg',
url: 'http://res.cloudinary.com/netlify/image/upload/e_grayscale,f_auto,q_auto,w_150/v1605632851/explorers/avatar.jpg',
secure_url: 'https://res.cloudinary.com/netlify/image/upload/e_grayscale,f_auto,q_auto,w_150/v1605632851/explorers/avatar.jpg'
}
]
}
在eager
属性下,显示了我们的转换以及查看转换后图像的完整 URL。
虽然对于像这样的简单转换,Node SDK 可能有点大材小用,但在我们开始查看添加视频品牌推广所需复杂转换时,它会变得非常方便。
使用 Cloudinary 转换视频
为了转换 Jamstack Explorers 中的视频,我们遵循相同的方法:每个视频都上传到 Cloudinary,然后我们修改 URL 以调整大小、调整质量并插入标题卡和缓冲区。
我们将要处理的一些主要转换类别是为了添加品牌推广
- 叠加
- 过渡
- 文字叠加
- 拼接
让我们看看每个类别,看看我们是否可以将 Jamstack Explorers 品牌重新应用到 Ben 的视频上!让我们通过设置index.js
来转换我们的基础视频来开始设置
cloudinary.uploader
.explicit('explorers/bumper', {
// these two properties match the beginning of the URL:
// https://res.cloudinary.com/netlify/image/upload/...
// ^^^^^^^^^^^^
resource_type: 'video',
type: 'upload',
// "eager" means we want to run these transformations ahead of time to avoid
// a slow first load time
eager: [
{
fetch_format: 'auto',
quality: 'auto',
height: 360,
width: 640,
crop: 'fill', // avoid letterboxing if videos are different sizes
},
],
// allow this transformed image to be cached to avoid re-running the same
// transformations over and over again
overwrite: false,
})
.then((result) => {
console.log(result);
});
您可能已经注意到,我们使用了一个名为“bumper”的视频,而不是 Ben 的原始视频。这是由于 Cloudinary 将视频组合在一起的方式。我们将在下一节中添加 Ben 的视频!
使用 Cloudinary 合并两个视频并添加自定义过渡效果
要添加我们的缓冲区,我们需要在 eager
数组中添加第二个转换“层”,该层将第二个视频作为叠加层添加。
为此,我们使用 overlay
转换并将其设置为 video:publicID
,其中 publicID
是 Cloudinary 资产的公共 ID,任何斜杠 (/
) 都转换为冒号 (:
)。
我们还需要告诉 Cloudinary 如何在两个视频之间进行过渡,我们使用一种名为亮度蒙版的特殊视频来实现,它允许我们用视频的黑色区域遮蔽一个视频,用白色区域遮蔽第二个视频。这样会产生一种风格化的交叉淡入淡出效果。
这是亮度蒙版本身的样子
视频和过渡都有自己的转换,这意味着我们需要在 Cloudinary 转换中将它们视为不同的“层”。这意味着将它们拆分为单独的对象,然后添加其他对象来“应用”每一层,这使我们能够宣布该部分完成并继续向主视频添加更多转换。
要告诉 Cloudinary 这是一个亮度蒙版而不是另一个视频,我们将 effect
类型设置为 transition
。
在 index.js
中进行以下更改以将所有这些内容到位
const videoBaseTransformations = {
fetch_format: 'auto',
quality: 'auto',
height: 360,
width: 600,
crop: 'fill',
}
cloudinary.uploader
.explicit('explorers/bumper', {
// these two properties match the beginning of the URL:
// <https://res.cloudinary.com/netlify/image/upload/>...
//
resource_type: 'video',
type: 'upload',
// "eager" means we want to run these transformations ahead of time to avoid
// a slow first load time
eager: [
videoBaseTransformations,
{
overlay: 'video:explorers:LCA-07-lifecycle-hooks',
...videoBaseTransformations,
},
{
overlay: 'video:explorers:transition',
effect: 'transition',
},
{ flags: 'layer_apply' }, // <= apply the transformation
{ flags: 'layer_apply' }, // <= apply the actual video
],
// allow this transformed image to be cached to avoid re-running the same
// transformations over and over again
overwrite: false,
})
.then((result) => {
console.log(result);
});
我们需要在所有视频上使用相同的格式、质量和大小转换,因此我们将它们提取到名为 videoBaseTransformations
的变量中,然后添加第二个对象来包含叠加层。
如果我们使用 node index.js
运行此代码,我们将获得如下所示的视频
还不错!这看起来已经像是 Jamstack Explorers 站点的一部分了,并且该过渡在从通用缓冲区到自定义视频之间添加了流畅的过渡效果。
添加结尾缓冲区的工作原理完全相同:我们需要为结尾缓冲区添加另一个叠加层和一个过渡。在本教程中,我们不会展示此代码,但如果您有兴趣,可以在源代码中查看。
使用文本叠加层为视频添加标题卡
要添加标题卡,有两个不同的步骤
- 提取一段简短的视频剪辑作为标题卡背景
- 添加带有视频标题的文本叠加层
接下来的两个部分将分别逐步介绍每个步骤,以便我们了解这两个步骤之间的区别。
提取短视频剪辑作为标题卡背景
当 Adam Hald 创建 Explorers 视频资产时,他包含了一个 美丽的介绍视频 ,它以星空为开头,非常适合作为标题卡。使用 Cloudinary,我们可以获取几秒钟的星空并将其拼接成每个视频作为标题卡!
在 index.js
中,添加以下转换块
cloudinary.uploader
.explicit('explorers/bumper', {
// these two properties match the beginning of the URL:
// https://res.cloudinary.com/netlify/image/upload/...
//
resource_type: 'video',
type: 'upload',
// "eager" means we want to run these transformations ahead of time to avoid
// a slow first load time
eager: [
videoBaseTransformations,
{
overlay: 'video:explorers:LCA-07-lifecycle-hooks',
...videoBaseTransformations,
},
{
overlay: 'video:explorers:transition',
effect: 'transition',
},
{ flags: 'layer_apply' }, // <= apply the transformation
{ flags: 'layer_apply' }, // <= apply the actual video
// add the outro bumper and a transition
{
overlay: 'video:explorers:countdown',
...videoBaseTransformations,
},
{
overlay: 'video:explorers:transition',
effect: 'transition',
},
{ flags: 'layer_apply' },
{ flags: 'layer_apply' },
// splice a title card at the beginning of the video
{
overlay: 'video:explorers:intro',
flags: 'splice', // splice this into the video
...videoBaseTransformations,
},
{
audio_codec: 'none', // remove the audio
end_offset: 3, // shorten to 3 seconds
effect: 'accelerate:-25', // slow down 25% (to ~4 seconds)
},
{
flags: 'layer_apply',
start_offset: 0, // put this at the beginning of the video
},
],
// allow this transformed image to be cached to avoid re-running the same
// transformations over and over again
overwrite: false,
})
.then((result) => {
console.log(result);
});
使用 splice
标志,我们告诉 Cloudinary 直接添加此视频,无需过渡。
在下一组转换中,我们添加了三个以前从未见过的转换
- 我们将
audio_codec
设置为none
以删除此视频片段的声音。 - 我们将
end_offset
设置为3
,这意味着我们只会获得视频的前 3 秒。 - 我们添加了
accelerate
效果,其值为-25
,这会将视频速度降低 25%。
运行 node index.js
现在将为我们提供一个视频,该视频以不到 4 秒的无声星空开头
使用 Cloudinary 添加文本叠加层
我们的最后一步是添加文本叠加层以显示视频标题!
文本叠加层使用与其他叠加层相同的 overlay
属性,但我们传递一个包含字体设置的对象。Cloudinary 支持各种字体——我还没有找到确定的列表,但似乎有很多 Google 字体——如果您已购买自定义字体的许可证,则可以 将自定义字体上传到 Cloudinary 以用于文本叠加层 。
cloudinary.uploader
.explicit('explorers/bumper', {
// these two properties match the beginning of the URL:
// <https://res.cloudinary.com/netlify/image/upload/>...
//
resource_type: 'video',
type: 'upload',
// "eager" means we want to run these transformations ahead of time to avoid
// a slow first load time
eager: [
videoBaseTransformations,
{
overlay: 'video:explorers:LCA-07-lifecycle-hooks',
...videoBaseTransformations,
},
{
overlay: 'video:explorers:transition',
effect: 'transition',
},
{ flags: 'layer_apply' }, // <= apply the transformation
{ flags: 'layer_apply' }, // <= apply the actual video
// add the outro bumper and a transition
{
overlay: 'video:explorers:countdown',
...videoBaseTransformations,
},
{
overlay: 'video:explorers:transition',
effect: 'transition',
},
{ flags: 'layer_apply' },
{ flags: 'layer_apply' },
// splice a title card at the beginning of the video
{
overlay: 'video:explorers:intro',
flags: 'splice', // splice this into the video
...videoBaseTransformations,
},
{
audio_codec: 'none', // remove the audio
end_offset: 3, // shorten to 3 seconds
effect: 'accelerate:-25', // slow down 25% (to ~4 seconds)
},
{
overlay: {
font_family: 'roboto', // lots of Google Fonts are supported
font_size: 40,
text_align: 'center',
text: 'Lifecycle Hooks', // this can be any text you want
},
width: 500,
crop: 'fit',
color: 'white',
},
{ flags: 'layer_apply' },
{
flags: 'layer_apply',
start_offset: 0, // put this at the beginning of the video
},
],
// allow this transformed image to be cached to avoid re-running the same
// transformations over and over again
overwrite: false,
})
.then((result) => {
console.log(result);
});
除了设置字体大小和对齐方式外,我们还应用了 500px 的宽度(默认情况下将居中)以防止标题文本挤压到标题卡的侧面,并将 crop
值设置为 fit
,这将换行较长的标题。将 color
设置为 white
使我们的文本在深色的星空背景下可见。
运行 node index.js
生成 URL,我们将看到我们完全品牌的视频,包括标题卡和缓冲区!
构建您的视频品牌一次,随处使用
创建缓冲区、过渡和标题卡需要大量工作。创建高质量的视频内容也需要大量工作。如果我们必须手动编辑每个 Jamstack Explorers 视频以插入这些标题卡和缓冲区,那么我们极不可能真正做到这一点。
我们知道,使视频保持一致品牌的唯一现实方法是 减少添加品牌标识的阻力 ,而 Cloudinary 让我们完全自动化了这一过程。这意味着我们无需任何手动步骤即可保持一致!
此外,这也意味着如果我们将来更新标题卡或缓冲区,我们可以通过更改一个位置的代码来更新所有视频的所有品牌标识。这对我们来说是一个巨大的缓解,因为我们知道 Explorers 将随着时间的推移继续发展和演变。
接下来做什么
现在您已经了解了如何使用 Cloudinary 添加自定义品牌标识,以下是一些其他资源,可帮助您继续学习。
- 查看本教程的 源代码。
- 查看 Jamstack Explorers 视频品牌标识的源代码。
- 详细了解 Cloudinary 的视频转换 API。
- 了解 Jamstack Explorers 上的 Web 开发 Jamstack Explorers。
- 了解如何 使用 Cloudinary 创建自定义社交媒体图片。
- 观看 Ben 的完整任务 以了解所有内容是如何组合在一起的!
您还可以使用 Cloudinary 自动化哪些其他操作?通过自动化视频编辑工作流程的重复部分,您可以节省多少时间?我恰好是那种喜欢谈论这些事情的极客,所以 在 Twitter 上向我发送您的想法!
解释很棒
只是一个快速的问题:在图像/视频转换方面,是否有任何方法例如在上传后将 CSS 过滤器应用到其中?