根据 Firebase Cloud Functions 文档,您可以在云函数中利用 ImageMagick: https ://firebase.google.com/docs/functions/use-cases
是否可以做类似的事情但调用 FFMPEG 而不是 ImageMagick?虽然缩略图图像很棒,但我还希望能够将传入的图像附加到存储在 Firebase Storage 上的视频文件中。
根据 Firebase Cloud Functions 文档,您可以在云函数中利用 ImageMagick: https ://firebase.google.com/docs/functions/use-cases
是否可以做类似的事情但调用 FFMPEG 而不是 ImageMagick?虽然缩略图图像很棒,但我还希望能够将传入的图像附加到存储在 Firebase Storage 上的视频文件中。
更新:ffmpeg
现在预安装在 Cloud Functions 环境中。有关预装软件包的完整列表,请查看https://cloud.google.com/functions/docs/reference/system-packages。
注意:您只有在/tmp/
.
该模块使用易于使用的 Node.js 模块抽象了 ffmpeg 命令行选项。
const ffmpeg = require('fluent-ffmpeg');
let cmd = ffmpeg('example.mp4')
.clone()
.size('300x300')
.save('/tmp/smaller-file.mp4')
.on('end', () => {
// Finished processing the video.
console.log('Done');
// E.g. return the resized video:
res.sendFile('/tmp/smaller-file.mp4');
});
因为ffmpeg
已经安装,您可以通过 shell 进程调用二进制文件及其命令行选项。
const { exec } = require("child_process");
exec("ffmpeg -i example.mp4", (error, stdout, stderr) => {
//ffmpeg logs to stderr, but typically output is in stdout.
console.log(stderr);
});
如果您需要特定版本的 ffmpeg,您可以包含一个 ffmpeg 二进制文件作为上传的一部分,然后使用类似child_process.exec
. 您需要为目标平台 (Ubuntu) 编译的 ffmpeg 二进制文件。
./
../
index.js
ffmpeg
const { exec } = require("child_process");
exec("ffmpeg -i example.mp4", (error, stdout, stderr) => {
//ffmpeg logs to stderr, but typically output is in stdout.
console.log(stderr);
});
我在 GitHub 上包含了两个完整的工作示例。这些示例适用于 Google Cloud Functions(不是专门针对 Firebase 的 Cloud Functions)。
虽然从技术上讲,您可以在 Firebase Functions 实例上运行 FFMPEG,但您很快就会达到小的配额限制。
根据这个答案,您可以改为使用函数来触发对 GCP 更强大的 App Engine 或 Compute Engine 服务的请求。App Engine 进程可以从同一个存储桶中抓取文件,处理转码,并将完成的文件上传回存储桶。如果您检查链接上的其他答案,一位用户发布了一个示例存储库,就是这样做的。
ffmpeg
现在包含在 Cloud Functions 环境中,因此可以直接使用:
spawn(
'ffmpeg',
['-i', 'video.mp4']
)
已安装软件包的完整列表:https ://cloud.google.com/functions/docs/reference/nodejs-system-packages
使用 lib https://github.com/eugeneware/ffmpeg-static
const ffmpeg = require('fluent-ffmpeg');
const ffmpeg_static = require('ffmpeg-static');
let cmd = ffmpeg.('filePath.mp4')
.setFfmpegPath(ffmpeg_static.path)
.setInputFormat('mp4')
.output('outputPath.mp4')
...
...
.run()
实际上,没有。FFMPEG 处理通常超过Cloud Functions 配额(10MB 上传)的音频/视频文件。
您需要在 GCP 的 AppEngine 上运行Node.js。
/**
* Copyright 2017 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for t`he specific language governing permissions and
* limitations under the License.
*/
'use strict';
const functions = require('firebase-functions');
const gcs = require('@google-cloud/storage')();
const path = require('path');
const os = require('os');
const fs = require('fs');
const ffmpeg = require('fluent-ffmpeg');
const ffmpeg_static = require('ffmpeg-static');
/**
* When an audio is uploaded in the Storage bucket We generate a mono channel audio automatically using
* node-fluent-ffmpeg.
*/
exports.generateMonoAudio = functions.storage.object().onChange(event => {
const object = event.data; // The Storage object.
const fileBucket = object.bucket; // The Storage bucket that contains the file.
const filePath = object.name; // File path in the bucket.
const contentType = object.contentType; // File content type.
const resourceState = object.resourceState; // The resourceState is 'exists' or 'not_exists' (for file/folder deletions).
const metageneration = object.metageneration; // Number of times metadata has been generated. New objects have a value of 1.
// Exit if this is triggered on a file that is not an audio.
if (!contentType.startsWith('audio/')) {
console.log('This is not an audio.');
return;
}
// Get the file name.
const fileName = path.basename(filePath);
// Exit if the audio is already converted.
if (fileName.endsWith('_output.flac')) {
console.log('Already a converted audio.');
return;
}
// Exit if this is a move or deletion event.
if (resourceState === 'not_exists') {
console.log('This is a deletion event.');
return;
}
// Exit if file exists but is not new and is only being triggered
// because of a metadata change.
if (resourceState === 'exists' && metageneration > 1) {
console.log('This is a metadata change event.');
return;
}
// Download file from bucket.
const bucket = gcs.bucket(fileBucket);
const tempFilePath = path.join(os.tmpdir(), fileName);
// We add a '_output.flac' suffix to target audio file name. That's where we'll upload the converted audio.
const targetTempFileName = fileName.replace(/\.[^/.]+$/, "") + '_output.flac';
const targetTempFilePath = path.join(os.tmpdir(), targetTempFileName);
const targetStorageFilePath = path.join(path.dirname(filePath), targetTempFileName);
return bucket.file(filePath).download({
destination: tempFilePath
}).then(() => {
console.log('Audio downloaded locally to', tempFilePath);
// Convert the audio to mono channel using FFMPEG.
const command = ffmpeg(tempFilePath)
.setFfmpegPath(ffmpeg_static.path)
.audioChannels(1)
.audioFrequency(16000)
.format('flac')
.on('error', (err) => {
console.log('An error occurred: ' + err.message);
})
.on('end', () => {
console.log('Output audio created at', targetTempFilePath);
// Uploading the audio.
return bucket.upload(targetTempFilePath, {destination: targetStorageFilePath}).then(() => {
console.log('Output audio uploaded to', targetStorageFilePath);
// Once the audio has been uploaded delete the local file to free up disk space.
fs.unlinkSync(tempFilePath);
fs.unlinkSync(targetTempFilePath);
console.log('Temporary files removed.', targetTempFilePath);
});
})
.save(targetTempFilePath);
});
});
https://github.com/firebase/functions-samples/blob/master/ffmpeg-convert-audio/functions/index.js
建议 App Engine 的其他答案是正确的。但是缺少的信息是 App Engine 是什么?
它基本上是举重者。它允许您编写后端并将其部署到云中。想想您通常开发的 Node express 服务器。然后将其部署到云端。那就是应用引擎。
Firebase / Cloud Functions 通常通过 HTTP 或通过 PubSub 与 App Engine 通信。
函数适用于轻量级工作。它们会告诉您事件何时发生(例如文件上传到存储桶),并且触发的“事件”有一个有效负载详细说明有关事件的信息(例如上传到存储桶的对象的详细信息)。
当该事件发生时,如果需要繁重的工作(或者如果缺少 Node.js 运行时环境中所需的软件),该函数会向 App Engine 发出 HTTP 请求,提供 App Engine 执行必要操作所需的信息加工。
App Engine 很灵活。您定义一个 yaml 文件和一个可选的 Dockerfile。
这是一个例子:
runtime: custom # custom means it uses a Dockerfile
env: flex
manual_scaling:
instances: 1
resources:
cpu: 1
memory_gb: 0.5
disk_size_gb: 10
这里你定义CPU个数、内存、磁盘大小等。与函数不同,磁盘是可写的(我被误导了,我还在整合过程中)。
通过 Dockerfile,您可以准确定义要安装的软件。如果你不熟悉 Dockerfile,这里有一个很好的例子。
https://nodejs.org/en/docs/guides/nodejs-docker-webapp
您在本地进行开发,然后在完成后部署到云:
gcloud app deploy
瞧,您的应用程序出现在云端。该gcloud
命令随Google Cloud SDK一起提供。
请注意,AppEngine 可以在处理完成后通过 HTTP 函数或 PubSub 与函数对话。
在他里面有很多的爱 :D