如何使用aws 的serverless 的lambda 来实现ffmpeg的截图

## 描述:
	业务需求在aws云上面实现直播,随机截取直播流进行截图,上传到S3桶里面去。
	业务其实很简单,难点是在如何在serverless的lambda来部署ffmpeg,因为lambda的无状态特性,所以我们无法直接在lambda上安装ffmpeg的安装包。
这就让我们在aws的lambda上部署ffmpeg变的困难起来,但是方法总比困难多,我有两个解决方案:
1. 通过docker 部署到aws 的lambda 
2. 通过自定义层的方法部署到lambda
今天这里我只讲第二种方法,因为第二种方法及其的简单高效,还可以看到自己写的代码,比docker友好很多,本篇文章分两段前面一段是用来介绍如何用lambda搭建ffmpeg,后面一段是用来用来介绍ffmpeg如果截取视频流。

通过自定义层的方式来部署ffmpeg到lambda

准备事项:
  1. 准备一台与lambda系统相同的ec2(临时使用用完就可以delete掉了)
  2. 创建一个lambda用的layer
  3. 创建一个lambda

1.准备一台与lambda系统相同的ec2

python ffmpeg推流帧 python ffmpeg拉流_aws


上图是与lambda版本相同的linux系统

这台ec2是用来下载ffmpeg,解压重新压缩ffmpeg,是为lambda准备ffmpeg层的,这些任务结束后这个ec2就可以删除掉了

上命令:

这4条命令是用来下载ffmpeg和使用md5来检查文件和解压ffmpeg安装包

wget https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz
wget https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz.md5
md5sum -c ffmpeg-release-amd64-static.tar.xz.md5
tar xvf ffmpeg-release-amd64-static.tar.xz

FFmpeg 二进制文件位于文件夹“ffmpeg-4.3.1-amd64-static”中
为 Lambda 层创建一个 ZIP 包,如下所示:

mkdir -p ffmpeg/bin
cp ffmpeg-4.3.1-amd64-static/ffmpeg ffmpeg/bin/
cd ffmpeg
zip -r ../ffmpeg.zip .

好!我们可以通过WinSCP讲ffmpeg.zip下载到本地或通过aws cli 上传到指定的s3中

2.创建lambda的layer

python ffmpeg推流帧 python ffmpeg拉流_aws_02


填写完名称和描述,上传我们压缩好的ffmpeg.zip文件,选择兼容运行的version,层的许可证避免被函

好那么我们的最重要的一点层就创建出来了。

3.创建一个lambda函数

创建一个lambda函数来运行我们和ffmpeg相关的代码

1). 创建一个lambda函数

python ffmpeg推流帧 python ffmpeg拉流_python ffmpeg推流帧_03


2.绑定我们在第一步创建的lambda layer

python ffmpeg推流帧 python ffmpeg拉流_aws_04


3. build我们的代码到指定的lambda函数上面

python ffmpeg推流帧 python ffmpeg拉流_aws_05


好!到此我们的lambda部署ffmpeg的步骤就全部说完了,我们接下来介绍一下我们是如何实现截取直播流截图的

通过lambda + ffmpeg 来实现视频流截图

我们的直播途径是aws ivs(Amazon Interactive Video Service),不的不说这是一个非常快的来实现直播的一种方案,当我们开始直播的时候
ivs会给我们一个m3u8的直播连接。这时候我们可以通ffmpeg来读取直播stream,获取stream里面的指定/随机的一帧进行截图。
下面是一个简单直播的架构图:

python ffmpeg推流帧 python ffmpeg拉流_python ffmpeg推流帧_06


在这里插入图片描述接下来我就简单的介绍一下这个简单的架构图

cloudfront: CND 全球边缘站点用于缓存直播图片等信息

api gateway: 他可以使我们通过internet 对lambda中的api 进行访问

vpc: 我们的所有服务都在aws的vpc下进行部署

video_lambda: 直播api 应用层lambda 用于调用ivs负责 用户登录注册,ivs开播、下播,数据的存储

ffmpeg_lambda: 开播后负责对直播stream进行截图

factor_lambda: 对图片进行动态的分辨率的改变

今天我们讲的就是inplay_ffmpeg这个lambda,这个lambda的视频流url是通过lambda的event来传递给该lambda的,该lambda获取到event中的url
会通过ffmpeg对视频流进行截图,下面我们介绍一下ffmpeg命令
/opt/bin/ffmpeg -probesize 32768 -i {pull_stream_url} -y -t 0.001 -ss 1 -f image2 -r 1 -
/opt/bin/ffmpeg :是ffmpeg的执行开头,为什么要加opt/bin呢,是因为在lambda中我们的执行package在 /opt/bin文件夹下面的
-probesize 32768:以字节为单位设置探测大小,即分析获取流信息的数据大小
-i: 输入
-y: 无需询问即可覆盖输出文件
-t: 当用作输入选项(之前-i)时,限制从输入文件读取数据 的持续时间。我这里设置的是0.001秒
-ss:开始时间。
-f:强制输入或输出文件格式。 
image2: 图像文件解复用器。
-r:  提取图像的频率
-: 输出

以上命令的意思是当ffmpeg获取到m3u8的视频stream链接的时候通过我设定的开始时间和截取频率对stream进行截图并按照指定的格式进行输出,最后获取到stream上传到s3桶里面。
code 如下:

# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0 (https://spdx.org/licenses/MIT-0.html)

import json
import os
import subprocess
import shlex
import boto3
import time

S3_DESTINATION_BUCKET = "inplay-video-image"

def lambda_handler(event, context):

    if not event:
        print("event is null")
        return False
    time.sleep(30)
    s3_client = boto3.client('s3')
    file_name = "{file_path}/covers.jpeg".format(file_path = event.get('user_id'))

    ffmpeg_cmd = "/opt/bin/ffmpeg -probesize 32768 -i {pull_stream_url} -y -t 0.001 -ss 1 -f image2 -r 1 -".format(pull_stream_url = event.get("pull_stream_url"))
    print(ffmpeg_cmd)
    command1 = shlex.split(ffmpeg_cmd)
    p1 = subprocess.run(command1, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    print(p1.stdout)
    resp = s3_client.put_object(Body=p1.stdout, Bucket=S3_DESTINATION_BUCKET, Key=file_name)

    return {
        'statusCode': 200,
        'body': json.dumps('Processing complete successfully')
    }

这种方案并不是唯一的解决方案,所以并不是绝对正确的,这个文章主要讲如何用lambda + ffmpeg 快速部署一个具有ffmpeg层的lambda 来实现serverless的一种方法。