在云转码源码项目中我使用 AWS 资源来自动化转码 4K 视频文件的过程。结合 S3、Lambda、Elastic Transcoder 管道和 SNS,我能够毫不费力地完成这项任务。一旦文件上传到 S3 源存储桶,它就会被转换并保存在转码后的完整存储桶中。SNS 通知此过程的启动和完成。这对于图像处理也非常有用。以下是我为完成此任务而采取的步骤,以及完成后的发现。
演示:y.jcedus.top
在开始云转码源码项目之前,我创建了启动所需的初始资源。首先,我创建了 3 个单独的 S3 存储桶并附加了存储桶策略以允许公开访问它们。一个用于源上传内容的存储桶,一个用于已完成转码文件的存储桶,最后一个用于保存缩略图的存储桶。
server.js
var express = require('express');
var resumable = require('../util/resumable-node.js')('/tmp/cloud_uploads/');
var app = express();
var multipart = require('connect-multiparty');
var crypto = require('crypto');
var fs = require('fs');
var kue = require('kue');
var db = require('./db');
var ffprobe = require('ffprobe');
var ffprobeStatic = require('ffprobe-static');
var NRP = require('node-redis-pubsub');
var redis_client = require('redis').createClient(7776);
const { fork, spawn } = require('child_process');
const constants = require("./constants");
// store kue's job id to redis so we can cancel tasks in constant time
redis_client.on('connect', function() {
console.log('connected');
});
// worker queue
var queue = kue.createQueue({
redis: {
port: 7776,
host: "127.0.0.1"
}
});
for (var i = 0; i < constants.NUMBER_OF_WORKER_THREADS; ++i) {
console.log("forked!");
fork('./app/worker');
}
// message queue
var nrp = new NRP({
port: 7776,
scope: "msg_queue"
});
下一步是创建和订阅 SNS 主题。对于这个协议,我选择使用电子邮件和 SMS 通知。完成后,我创建了订阅并验证了我的电子邮件。您还可以通过 SNS 发布测试消息,以验证所有通知源都在接收消息。
通过初始源设置,现在是时候创建和启动我的 Elastic Transcoder 管道了。我将其命名,然后将我的输入存储桶设置为我的 S3 源存储桶。对于转码文件,我选择了我的转码完成存储桶,最后对于缩略图,我选择了我的缩略图 S3 存储桶。管道现已启动,我记下了我的管道 ID 以供以后使用。
现在开始创建 Lambda 函数。我从头开始创建函数,并在运行时使用 Python 3.7 (Boto3 SDK)。对于一个角色,我创建了一个自定义角色和策略并创建了我的 Lambda 函数。
此时我为我的函数配置了一个 S3 触发器。我将源存储桶设为触发器并选择了“所有对象创建事件”。本质上,这将向 Lambda 传达已添加文件并且我的函数有任务要完成。现在是时候将功能代码添加到编辑器中并保存了。最后,我添加了一个环境变量,并设置了键“PIPELINE_ID”和实际 ID 的值。
现在终于到了测试的时候了。我下载了一些简短的 4K 剪辑并将第一个上传到我的 S3 源存储桶。我通过短信和电子邮件收到通知,转码工作已经开始。完成时也会通知。这时我检查了我的转码完成桶和我的缩略图桶,并确认文件已上传。一切正常,我的简单转码项目就完成了。最后,我检查了我的 CloudWatch 日志文件并在那里进行了确认。
云转码源码构建和运行
docker build -t cloud .
docker run --rm --name cloud-postgres -v ~/kvazaar-cloud-db:/var/lib/postgresql/data -d --env POSTGRES_PASSWORD=postgres postgres:latest
docker run --rm --name cloud-encoder -d --link cloud-postgres:pg -p 80:8080 -p 443:8443 -v /tmp:/tmp cloud
如果要在 localhost 中使用 Kvazaar Cloud Encoder,则需要将 socket.js 中的变量httpsEnabled更改为 false 并运行:
docker run --rm --name cloud-postgres -v ~/kvazaar-cloud-db:/var/lib/postgresql/data -d --env POSTGRES_PASSWORD=postgres postgres:latest
docker run --rm --name cloud-encoder -d --link cloud-postgres:pg -p 80:8081 -v /tmp:/tmp cloud
云转码源码运作
用户(重新)使用 WebSocket 初始化与服务器的连接
用户向服务器发送选项和文件信息
服务器检查给定选项是否都有效
如果它们是 -> 文件上传被拒绝
服务器检查此文件是否已存在
如果是 -> 文件上传被拒绝
文件上传开始
文件上传完成后,服务器会检查上传的文件是视频并且 < FILE_TIME_LIMIT_IN_SECONDS (30min)
任务被添加到工作队列
其中一名工人在工作中接受任务,并向用户发送有关该任务的状态更新
任务准备就绪后,用户可以下载输出文件或删除任务
在任何时候,用户都可以取消请求并发出另一个请求。用户还可以发出多个并发请求(其中一些可能会排队)。
每个请求的下载限制为 FILE_DOWNLOAD_LIMIT (2),然后删除任务。
server.js
服务器负责处理文件上传和验证。
如果文件是视频文件并且其持续时间小于 FILE_TIME_LIMIT_IN_SECONDS(30 分钟),则该文件被视为有效。对于原始视频,限制为 FILE_SIZE_LIMIT_IN_BYTES (50GB)
Server.js 通过 socket.js 与用户进行通信。有关详细信息,请参阅下面的消息规范
它还产生 NUMBER_OF_WORKER_THREADS (5) 个工作线程和 1 个消息队列线程。
worker.js
Cloud 使用 NUMBER_OF_WORKER_THREADS 个工作线程来处理用户请求。Worker 监听 Kue 的消息队列以获取编码请求。
工作线程的工作原理非常简单:
检查给定的视频文件是否是 yuv420p 格式的原始文件
如果是,则无需预处理
如果不是:
如果输入是原始的 -> 将给定格式转换为 yuv420p
如果输入不是原始的 -> 使用 FFMPEG 提取原始视频(yuv420p)和音频(如有必要)
在视频末尾添加 Kvazaar 徽标
使用 Kvazaar 对原始视频进行编码
如果用户已指定,则将编码的视频添加到容器中
删除中间文件(提取的音轨和原始视频文件)
通知用户编码过程已准备就绪,可以下载文件
通过 socket.js 向用户发送有关编码过程的状态消息。
socket.js
Socket.js 为服务器和工作人员提供了一种与用户交互的方式。Worker 和服务器通过 Redis 向 socket.js 发送消息,socket.js 然后将这些消息发送给用户。
parser.js
Parser.js 负责解析和验证大多数用户输入。它处理文件相关信息(例如 FPS 和分辨率)以及 Kvazaar 选项。
db.js
db.js 为所有其他文件提供数据库 API。
客户端和服务器使用请求/回复对相互通信。例如,当用户单击“编码”按钮时,前端代码会向 socket.js 发送一个uploadRequest。然后 Socket 验证请求(“一切如何工作”部分)并在uploadReply消息中拒绝或批准上传。
系统使用的其他一些消息类型是:
前端发送的taskQuery,用于获取用户所做的所有任务
当用户取消正在进行的上传时,前端代码发送的cancelInfo
从 socket.js 发送到 worker.js 的内部cancelRequest以取消正在进行的任务