使用AWS Lambda构建基于ffmpeg的视频处理Serverless服务

背景

公司有一块业务需要对生成的视频做100倍快放处理。视频一般是6个小时。视频由某直播推流服务生成,保存在云端。对其进行快放处理使用了ffmpeg来操作,是一个计算密集型的操作,放在业务服务器上,容易在处理过程中造成业务服务器的性能下降。该业务处理并不是全天候的,一天大概十几例的样子,单独购买服务器也很不划算。以上是从成本考虑。

从该业务类型上考虑,该业务十分独立。给定原视频云存储地址,处理,保存到云存储上。和其他业务耦合性十分低。

结合以上两点,是现在的Serverless完美的适用场景。

下面将使用AWS的Lambda服务,结合AWS S3云存储来实现上述需求。

前置知识

  • 有亚马逊账号
  • 对AWS有一定了解
  • 对云存储有一定了解
  • 对JavaScript有一定了解
  • 对ffmpeg有一定了解

注册账号

AWS官网:https://aws.amazon.com/cn/
这是全球站,可以注册个人账号。

AWS在国内也有网站:https://www.amazonaws.cn
但是这里注册不了个人账号,只能注册企业账号。

在https://aws.zmazon.com/cn 注册账号的时候按照网站提示一步一步来就好了。最后会要绑定外币信用卡。

官方默认提供了很多免费服务。常规的主机,最低配置免费12个月,其他很多服务也在一定使用量情况下是免费的。本文中提到的Lambda服务和S3服务也都是在一定使用量情况下免费。

设置语言

我们使用的是全球站,虽然进入 https://aws.amazon.com/cn ,显示的都是中文,但是用里面的服务的时候,这些页面很多都是默认英文展示的。所以首先要设置语言为中文。
在这里插入图片描述然后出现下面的页面
在这里插入图片描述点击本地化和默认区域这一栏的编辑按钮,出现下面页面,选择中文(简体)即可。保存设置。
在这里插入图片描述以后访问的AWS页面,会默认提供中文页面。

设置区域

AWS有很多区域,我们在使用不同的服务的时候,尽量都在同一个区域内。一般都使用美国东部-弗吉尼亚北部这个区域。

设置S3云存储

S3是AWS的云存储服务。可以用来存储各种类型的资源。在当前文章中,会用来存储视频。

在这里插入图片描述创建Bucket(桶)
在这里插入图片描述在这里插入图片描述在当前案例中,只需要填写桶的名称就好了,其他配置想都是默认,注意区域,后面创建Lambda服务的时候也需要在同一个区域。点击最后的创建存储桶

在这里插入图片描述
点击进入该桶的详情页面。

在这里插入图片描述下面就是在该桶下创建了video文件夹,并在该文件夹内上传了一个视频文件
在这里插入图片描述到这里S3云存储的设置和使用就大致了解了。

设置Lambda

在这里插入图片描述进入Lambda页面后,注意 区域 必须和刚刚创建的S3 存储桶 在一个区域。点击创建函数。

在这里插入图片描述新建好的函数如下:
在这里插入图片描述示例代码是一段js,当前的基础环境就是nodejs-16,我们可以理解为当前这个所谓的函数是由一个Linux系统中的nodejs来执行的一段js代码。

这个函数的执行要有一个触发点,所以等会还要设置触发器。触发器就是S3服务中的相关桶的文件新增。

除了nodejs环境还不够,我们还要有ffmpeg环境,但是AWS提供的基础环境中没有。基础环境一般有nodejs、python、ruby、java等。在这里,ffmpeg并不是要我们去在一个Linux里面安装,这是Serverless服务,没有服务器的概念。

ffmpeg作为在基础环境中的,第二个层环境,AWS有固定的规范来进行设置。在这里我们可以在AWS提供的第三方的代码库中找到ffmpeg环境的层。

在这里插入图片描述在这里插入图片描述在这儿,我们找到了别人维护的ffmpeg的环境,可以和Lambda的基础环境兼容。也就是说可以作为一层加到 基础的环境上,组成一个新的环境。既有nodejs,又有ffmpeg。该作者还提供了实例代码,使用nodejs来调用系统的命令,执行ffmpeg相关的命令。查看源代码:https://github.com/serverlesspub/ffmpeg-aws-lambda-layer。该代码中存在一些小的错误。后面使用的代码经过修改。

我们进入这个兼容环境的详情页后,设置一个名称,就可以部署到自己的 里面使用了。
在这里插入图片描述下面是创建成功的页面,不用做任务动作
在这里插入图片描述我们回到Lambda主页,点击
在这里插入图片描述会发现,我们刚刚 使用别人 的部署 好的 层。
在这里插入图片描述点击进入,复制 该 层 的识别字符串
在这里插入图片描述

然后回到我们刚刚新建的 函数页面,拉到最下面。

在这里插入图片描述在这里插入图片描述最后点击 添加。

修改基础代码

在这里插入图片描述
图中的代码是按https://github.com/serverlesspub/ffmpeg-aws-lambda-layer 代码调整的。

  • 修改几处错误
  • 修改ffmpeg的处理命令

index.js


const s3Util = require('./s3-util'),
	childProcessPromise = require('./child-process-promise'),
	path = require('path'),
	os = require('os'),
	EXTENSION = process.env.EXTENSION,
	THUMB_WIDTH = process.env.THUMB_WIDTH,
	OUTPUT_BUCKET = process.env.OUTPUT_BUCKET,
	MIME_TYPE =  process.env.MIME_TYPE;

exports.handler = function (eventObject, context) {
	const eventRecord = eventObject.Records && eventObject.Records[0],
		inputBucket = eventRecord.s3.bucket.name,
		key = eventRecord.s3.object.key,
		id = context.awsRequestId,
		resultKey = key.replace(/\.[^.]+$/, EXTENSION),
		workdir = os.tmpdir(),
		inputFile = path.join(workdir,  id + path.extname(key)),
		outputFile = path.join(workdir, id + '-output.' + EXTENSION);


	console.log('converting', inputBucket, key, 'using', inputFile);
	return s3Util.downloadFileFromS3(inputBucket, key, inputFile)
		.then(() => childProcessPromise.spawn(
			'/opt/bin/ffmpeg',
			['-i', inputFile, ' -filter_complex ', ` '[0:v]setpts=0.04*PTS[v]' `, ' -map ', ` '[v]' `, outputFile],
			{env: process.env, cwd: workdir}
		))
		.then(() => s3Util.uploadFileToS3(OUTPUT_BUCKET, resultKey, outputFile, MIME_TYPE));
};

s3-util.js

/*global module, require, Promise, console */

const aws = require('aws-sdk'),
	fs = require('fs'),
	s3 = new aws.S3(),
	downloadFileFromS3 = function (bucket, fileKey, filePath) {
		'use strict';
		console.log('downloading', bucket, fileKey, filePath);
		return new Promise(function (resolve, reject) {
			const file = fs.createWriteStream(filePath),
				stream = s3.getObject({
					Bucket: bucket,
					Key: fileKey
				}).createReadStream();
			stream.on('error', reject);
			file.on('error', reject);
			file.on('finish', function () {
				console.log('downloaded', bucket, fileKey);
				resolve(filePath);
			});
			stream.pipe(file);
		});
	}, uploadFileToS3 = function (bucket, fileKey, filePath, contentType) {
		'use strict';
		console.log('uploading', bucket, fileKey, filePath);
		return s3.upload({
			Bucket: bucket,
			Key: fileKey,
			Body: fs.createReadStream(filePath),
			ACL: 'private',
			ContentType: contentType
		}).promise();
	};

module.exports = {
	downloadFileFromS3: downloadFileFromS3,
	uploadFileToS3: uploadFileToS3
};

child-process-promise.js


/*global module, require, console, Promise */
'use strict';
const childProcess = require('child_process'),
	spawnPromise = function (command, argsarray, envOptions) {
		return new Promise((resolve, reject) => {
			console.log('executing', command, argsarray.join(' '));
			const childProc = childProcess.spawn(command, argsarray, envOptions || {env: process.env, cwd: process.cwd()}),
				resultBuffers = [];
			childProc.stdout.on('data', buffer => {
				console.log(buffer.toString());
				resultBuffers.push(buffer);
			});
			childProc.stderr.on('data', buffer => console.error(buffer.toString()));
			childProc.on('exit', (code, signal) => {
				console.log(`${command} completed with ${code}:${signal}`);
				if (code || signal) {
					reject(`${command} failed with ${code || signal}`);
				} else {
					resolve(Buffer.concat(resultBuffers).toString().trim());
				}
			});
		});
	};
module.exports = {
	spawn: spawnPromise
};

添加触发器

在这里插入图片描述在这里插入图片描述这儿,为了保险起见,我们还需再见一个桶,用来存储处理后的文件。避免可能的递归调用风险。

配置权限

在这里插入图片描述
在这里插入图片描述在这里插入图片描述
点击 最后 的 附加策略 即可。

配置环境变量

函数在执行的时候,可以通过一些注入环境变量的方式,给这个函数以更多的灵活性。在上述js代码中,会从环境变量中获取

  • 生成文件的后缀:EXTENSION
  • 生成文件所要保存的桶:OUTPUT_BUCKET
  • 生成文件的MIME值:MIME_TYPE
    在这里插入图片描述

在这里插入图片描述

配置内存使用和超时时间

AWS的Lambda执行默认有时间限制 以及 内存使用限制。我们当前业务下,这些都太小了。内存应该改为2048M,超时设置为10分钟。这些需要根据具体业务,反复的调试来确定的。
在这里插入图片描述在这里插入图片描述

向S3上传mp4视频,触发执行函数处理视频

以下使生成的倍速视频文件。
在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值