diff --git a/.github/workflows/registry-publish.yml b/.github/workflows/registry-publish.yml index 6c1d746..d8c233a 100644 --- a/.github/workflows/registry-publish.yml +++ b/.github/workflows/registry-publish.yml @@ -1,29 +1,27 @@ -name: publish package to serverless-hub +name: auto try publish all on: - release: - types: [created] + push: + branches: + - V3 +env: + REGISTRY_TOKEN: ${{secrets.ALIBABA_REGISTRY_V3_PUBLISH_TOKEN}} jobs: - deploy: + auto-publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 + - uses: actions/setup-node@v2 with: - python-version: '3.x' - - uses: actions/setup-node@v1 + node-version: 16 + registry-url: https://registry.npmjs.org/ + - uses: actions/setup-python@v5 with: - node-version: 12 - - name: Install dependencies + python-version: '3.10' + - name: install s run: | - python -m pip install --upgrade pip - pip install setuptools wheel twine - pip install requests - - name: Publish package - env: - publish_token: ${{ secrets.alibaba_registry_publish_token }} + npm i @serverless-devs/s -g + - name: publish run: | - ls - python publish.py \ No newline at end of file + python auto-publish.py \ No newline at end of file diff --git a/auto-publish.py b/auto-publish.py new file mode 100644 index 0000000..d363964 --- /dev/null +++ b/auto-publish.py @@ -0,0 +1,37 @@ +# coding=utf-8 + +import os +import subprocess + +os.system("s registry login --token {}".format(os.environ["REGISTRY_TOKEN"])) + + +def search_publish_yaml(directory): + succ_app_list = [] + for root, dirs, files in os.walk(directory): + if "publish.yaml" in files: + d = os.path.join(os.getcwd(), root) + d = d.replace("/./", "/") + cmd = ["s", "registry", "publish"] + result = subprocess.run( + cmd, cwd=d, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True + ) + exit_code = result.returncode + print("publish {}; =====> ExitCode={}".format(d, exit_code)) + + if exit_code != 0: + errMsg = "" + errMsg += result.stdout + errMsg += result.stderr + if "当前版本号应当大于前一次版本号" not in errMsg: + print(errMsg) + else: + succ_app_list.append(d) + + print("\n\npublish success app list:\n") + for item in succ_app_list: + print("{} published success".format(item)) + + +# 在当前目录递归搜索 +search_publish_yaml(".") diff --git a/readme.md b/readme.md index 5784514..863fe65 100644 --- a/readme.md +++ b/readme.md @@ -12,7 +12,4 @@ - GetSprites: 功能强大雪碧图制作函数 ([案例代码](./ffmpeg-app/src/functions/get_sprites)) - VideoWatermark: 功能强大的视频添加水印功能 ([案例代码](./ffmpeg-app/src/functions/video_watermark)) - [基于 FFmpeg 实现音视频转码](./transcode/src): `s init video-transcode` -- [基于 FFmpeg 实现 HTTP 触发器触发音视频转码](./http-transcode/src): `s init http-video-transcode` - [基于 FC + Serverless Workflow + OSS + NAS + FFmpeg 实现的弹性高可用、并行处理的视频转码服务](./video-process-flow/src): `s init video-process-flow` -- [对直播视频流截图的应用](./rtmp-snapshot/src): `s init rtmp-snapshot` -- [一个对浏览器全景录制](./headless-ffmpeg/src): `s init headless-ffmpeg` diff --git a/update.list b/update.list deleted file mode 100644 index ec0910a..0000000 --- a/update.list +++ /dev/null @@ -1 +0,0 @@ -./video-process-flow \ No newline at end of file diff --git a/video-process-flow/hook/index.js b/video-process-flow/hook/index.js index dc925a9..10570e9 100644 --- a/video-process-flow/hook/index.js +++ b/video-process-flow/hook/index.js @@ -3,23 +3,20 @@ async function preInit(inputObj) { } async function postInit(inputObj) { - console.log(`\n _______ _______ __ __ _______ _______ _______ + console.log(`\n _______ _______ __ __ _______ _______ _______ | || || |_| || || || | | ___|| ___|| || _ || ___|| ___| - | |___ | |___ | || |_| || |___ | | __ + | |___ | |___ | || |_| || |___ | | __ | ___|| ___|| || ___|| ___|| || | | | | | | ||_|| || | | |___ | |_| | |___| |___| |_| |_||___| |_______||_______| `) - console.log(`\n Welcome to the ffmpeg-app application - This application requires to open these services: + console.log(`\n Welcome to the multimedia-process-flow-v3 application + This application requires to open these services: FC : https://fc.console.aliyun.com/ - This application can help you quickly deploy the ffmpeg-app project. - The application uses FC component:https://github.com/devsapp/fc - The application homepage: https://github.com/devsapp/start-ffmpeg\n`) + This application can help you quickly deploy the ffmpeg project. + The application uses FC component:https://docs.serverless-devs.com/user-guide/aliyun/#fc3\n`) - const { artTemplate } = inputObj; - artTemplate("code/flows/video-processing-fc.yml"); } module.exports = { diff --git a/video-process-flow/publish.yaml b/video-process-flow/publish.yaml index 26d1dbf..0e47f36 100644 --- a/video-process-flow/publish.yaml +++ b/video-process-flow/publish.yaml @@ -1,10 +1,12 @@ -Type: Application -Name: video-process-flow -Version: 0.1.6 +Edition: 3.0.0 +Type: Project +Name: multimedia-process-flow-v3 +Version: 0.0.3 Provider: - 阿里云 Description: 基于 FC + Serverless Workflow + OSS + NAS + FFmpeg 实现的弹性高可用、并行处理的视频转码服务 -HomePage: https://github.com/devsapp/start-ffmpeg/tree/master/video-process-flow +HomePage: https://github.com/devsapp/start-ffmpeg/tree/master/multimedia-process-flow +Effective: Public Tags: - flow - ffmpeg @@ -18,19 +20,16 @@ Service: - AliyunFCFullAccess 硬盘挂载: Authorities: - - AliyunNASFullAccess - VPC: + - AliyunFCServerlessDevsRolePolicy + 专有网络: Authorities: - - AliyunVPCFullAccess - OSS: + - AliyunFCServerlessDevsRolePolicy + 对象存储: Authorities: - - AliyunOSSFullAccess - 工作流: + - AliyunFCServerlessDevsRolePolicy + 云工作流: Authorities: - AliyunFnFFullAccess - 其它: - Authorities: - - AliyunECSFullAccess Parameters: type: object additionalProperties: false # 不允许增加其他属性 @@ -57,14 +56,29 @@ Parameters: - cn-shenzhen - ap-southeast-1 - us-west-1 - serviceName: - title: 服务名 + + flowName: + title: 工作流程名称 + type: string + default: multimedia-process-flow-${default-suffix} + description: Serverless 工作流流程名称 + + fnfRoleArn: + title: 工作流 RAM角色ARN type: string - default: video-process-flow-${default-suffix} - pattern: "^[a-zA-Z_][a-zA-Z0-9-_]{0,127}$" - description: 应用所属的函数计算服务 - serviceRoleArn: - title: 函数计算Service RAM角色ARN + default: "" + pattern: "^acs:ram::[0-9]*:role/.*$" + description: 应用所属的工作流需要的 role, 请提前创建好对应的 role, 授信工作流服务, 并配置好 AliyunFCInvocationAccess 和 AliyunFnFFullAccess policy。 + required: true + x-role: + name: fnf-execution-default-role + service: FNF + authorities: + - AliyunFCInvocationAccess + - AliyunFnFFullAccess + + functionRoleArn: + title: 函数角色 type: string default: "" pattern: "^acs:ram::[0-9]*:role/.*$" @@ -79,6 +93,7 @@ Parameters: - AliyunOSSFullAccess - AliyunFCDefaultRolePolicy - AliyunFnFFullAccess + ossBucket: title: 对象存储存储桶名 type: string @@ -124,23 +139,3 @@ Parameters: type: string default: mp4, flv, avi description: 转码后的视频格式,如果有需要输出多种格式, 使用逗号分隔 - - flowName: - title: 工作流程名称 - type: string - default: video-process-flow - description: Serverless 工作流流程名称 - - fnfRoleArn: - title: 工作流 RAM角色ARN - type: string - default: "" - pattern: "^acs:ram::[0-9]*:role/.*$" - description: 应用所属的工作流需要的 role, 请提前创建好对应的 role, 授信工作流服务, 并配置好 AliyunFCInvocationAccess 和 AliyunFnFFullAccess policy。 - required: true - x-role: - name: fnf-execution-default-role - service: FNF - authorities: - - AliyunFCInvocationAccess - - AliyunFnFFullAccess diff --git a/video-process-flow/readme.md b/video-process-flow/readme.md index 1105835..825e53a 120000 --- a/video-process-flow/readme.md +++ b/video-process-flow/readme.md @@ -1 +1,125 @@ -src/readme.md \ No newline at end of file + +> 注:当前项目为 Serverless Devs 应用,由于应用中会存在需要初始化才可运行的变量(例如应用部署地区、函数名等等),所以**不推荐**直接 Clone 本仓库到本地进行部署或直接复制 s.yaml 使用,**强烈推荐**通过 `s init ${模版名称}` 的方法或应用中心进行初始化,详情可参考[部署 & 体验](#部署--体验) 。 + +# multimedia-process-flow-v3 帮助文档 + + + +基于 FC + Serverless Workflow + OSS + NAS + FFmpeg 实现的弹性高可用、并行处理的视频转码服务 + + + + + + + + + + + + + + + +## 前期准备 + +使用该项目,您需要有开通以下服务并拥有对应权限: + + + + + +| 服务/业务 | 权限 | 相关文档 | +| --- | --- | --- | +| 函数计算 | AliyunFCFullAccess | [帮助文档](https://help.aliyun.com/product/2508973.html) [计费文档](https://help.aliyun.com/document_detail/2512928.html) | +| 硬盘挂载 | AliyunFCServerlessDevsRolePolicy | [帮助文档](https://help.aliyun.com/zh/nas) [计费文档](https://help.aliyun.com/zh/nas/product-overview/billing) | +| 专有网络 | AliyunFCServerlessDevsRolePolicy | [帮助文档](https://help.aliyun.com/zh/vpc) [计费文档](https://help.aliyun.com/zh/vpc/product-overview/billing) | +| 对象存储 | AliyunFCServerlessDevsRolePolicy | [帮助文档](https://help.aliyun.com/zh/oss) [计费文档](https://help.aliyun.com/zh/oss/product-overview/billing) | +| 云工作流 | AliyunFnFFullAccess | [帮助文档](undefined) [计费文档](undefined) | + + + + + + + + + + + + + + + +## 部署 & 体验 + + + +- :fire: 通过 [Serverless 应用中心](https://fcnext.console.aliyun.com/applications/create?template=multimedia-process-flow-v3) , + [![Deploy with Severless Devs](https://img.alicdn.com/imgextra/i1/O1CN01w5RFbX1v45s8TIXPz_!!6000000006118-55-tps-95-28.svg)](https://fcnext.console.aliyun.com/applications/create?template=multimedia-process-flow-v3) 该应用。 + + + + +- 通过 [Serverless Devs Cli](https://docs.serverless-devs.com/user-guide/install) 进行部署: + - [安装 Serverless Devs Cli 开发者工具](https://docs.serverless-devs.com/user-guide/install) ,并进行[授权信息配置]( https://docs.serverless-devs.com/user-guide/config) ; + - 初始化项目:`s init multimedia-process-flow-v3 -d multimedia-process-flow-v3` + - 进入项目,并进行项目部署:`cd multimedia-process-flow-v3 && s deploy -y` + + + +## 案例介绍 + + + +本案例将FFmpeg包装为一款音视频处理工作流,十分适用于加快视频转码处理速度的场景, 基本原理是对每一个视频,先进行切片处理,然后并行转码切片,最后合成,通过设置合理的切片时间,可以大大加速较大视频的转码速度。 + +FFmpeg 是音视频处理领域的一款强大工具,它可以被用于格式转换、编解码、录制和流处理等多种音视频相关任务。该技术因其高效性和多功能性,在视频处理、直播流媒体、数字媒体播放和编辑等多个领域中得到广泛应用。截至2023年4月,FFmpeg 在 GitHub 上的 star 数接近30,000,这反映了其在开源社区的受欢迎程度和影响力。被Netflix、YouTube、Facebook等多家知名企业采用,这些公司依赖 FFmpeg 处理其庞大的视频数据,以提供流畅的媒体播放体验、视频内容编辑和优化视频传输效率等服务。 + + + +## 使用流程 + + + +上传一个 mov 格式的视频到对象存储 bucket, OSS 触发器自动触发函数执行,函数调用工作流执行,工作流会同时进行 1 种或者多种格式的转码(由 s.yaml 中的 DST_FORMATS 参数控制), 本示例默认配置的是同时进行 mp4, flv, avi 格式的转码。 + +基于该工作流,您可以实现如下需求: + +1 一个视频文件可以同时被转码成各种格式以及其他各种自定义处理,比如增加水印处理或者在 after-process 更新信息到数据库等。 + +2 当有多个文件同时上传到 OSS,函数计算会自动伸缩, 并行处理多个文件, 同时每次文件转码成多种格式也是并行。 + +3 结合 NAS + 视频切片, 可以解决超大视频的转码, 对于每一个视频,先进行切片处理,然后并行转码切片,最后合成,通过设置合理的切片时间,可以大大加速较大视频的转码速度。 + +![image](https://img.alicdn.com/tfs/TB1A.PSzrj1gK0jSZFuXXcrHpXa-570-613.png) + + +**操作视频教程:** + +[![Watch the video](https://img.alicdn.com/imgextra/i2/O1CN01XvnqJu1XLS8SAU7LT_!!6000000002907-2-tps-250-155.png)](http://devsapp.functioncompute.com/video/multimedia-process-flow.mp4) + +**P.S.** 当您想要仅在一个简单的函数中直接完成视频处理逻辑时,可以参考[音视频转码 Job](https://github.com/devsapp/start-ffmpeg/tree/V3/transcode/src) + + + +## 注意事项 + + + + + + + + +## 开发者社区 + +您如果有关于错误的反馈或者未来的期待,您可以在 [Serverless Devs repo Issues](https://github.com/serverless-devs/serverless-devs/issues) 中进行反馈和交流。如果您想要加入我们的讨论组或者了解 FC 组件的最新动态,您可以通过以下渠道进行: + +

+ +| | | | +| --------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | +|

微信公众号:`serverless`
|
微信小助手:`xiaojiangwh`
|
钉钉交流群:`33947367`
| +

+
diff --git a/video-process-flow/src/code/flows/input-fc.json b/video-process-flow/src/code/flows/input-fc.json deleted file mode 100644 index 6cc5e2f..0000000 --- a/video-process-flow/src/code/flows/input-fc.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "oss_bucket_name": "fnf-test", - "video_key": "fnf_video/inputs/fc-official-short.mov", - "output_prefix": "fnf_video/outputs/fc/1", - "segment_time_seconds": 15, - "dst_formats": ["mp4", "flv"] -} \ No newline at end of file diff --git a/video-process-flow/src/code/flows/video-processing-fc.yml b/video-process-flow/src/code/flows/video-processing-fc.yml deleted file mode 100644 index c740d43..0000000 --- a/video-process-flow/src/code/flows/video-processing-fc.yml +++ /dev/null @@ -1,72 +0,0 @@ -version: v1beta1 -type: flow -steps: - - type: task - name: Split - resourceArn: 'acs:fc:::services/{{serviceName}}/functions/split' - retry: - - errors: - - FC.ResourceThrottled - - FC.ResourceExhausted - - FC.InternalServerError - - FnF.TaskTimeout - - FC.Unknown - intervalSeconds: 3 - maxAttempts: 16 - multiplier: 2 - - type: foreach - name: ParallelTranscode - iterationMapping: - collection: $.dst_formats - index: index - item: target_type - steps: - - type: foreach - name: Transcode_splits - iterationMapping: - collection: $.split_keys - index: index - item: split_video_key - steps: - - type: task - name: Transcode - resourceArn: 'acs:fc:::services/{{serviceName}}/functions/transcode' - retry: - - errors: - - FC.ResourceThrottled - - FC.ResourceExhausted - - FC.InternalServerError - - FnF.TaskTimeout - - FC.Unknown - intervalSeconds: 3 - maxAttempts: 16 - multiplier: 2 - - type: task - name: Merge - resourceArn: 'acs:fc:::services/{{serviceName}}/functions/merge' - retry: - - errors: - - FC.ResourceThrottled - - FC.ResourceExhausted - - FC.InternalServerError - - FnF.TaskTimeout - - FC.Unknown - intervalSeconds: 3 - maxAttempts: 16 - multiplier: 2 - outputMappings: - - target: video_proc_dir - source: $input.video_proc_dir - - type: task - name: after-process - resourceArn: 'acs:fc:::services/{{serviceName}}/functions/after-process' - retry: - - errors: - - FC.ResourceThrottled - - FC.ResourceExhausted - - FC.InternalServerError - - FnF.TaskTimeout - - FC.Unknown - intervalSeconds: 3 - maxAttempts: 16 - multiplier: 2 \ No newline at end of file diff --git a/video-process-flow/src/code/merge/index.py b/video-process-flow/src/code/merge/index.py index 943a73b..b4fbcf2 100644 --- a/video-process-flow/src/code/merge/index.py +++ b/video-process-flow/src/code/merge/index.py @@ -59,10 +59,10 @@ def handler(event, context): output_prefix = evt['output_prefix'] video_type = evt['target_type'] video_process_dir = evt['video_proc_dir'] - + transcoded_split_keys = [] for k in split_keys: - fileDir, shortname, extension = get_fileNameExt(k) + fileDir, shortname, extension = get_fileNameExt(k['filekey']) transcoded_filename = 'transcoded_%s.%s' % (shortname, video_type) transcoded_filepath = os.path.join(fileDir, transcoded_filename) transcoded_split_keys.append(transcoded_filepath) @@ -75,12 +75,12 @@ def handler(event, context): if len(transcoded_split_keys) == 0: raise Exception("no transcoded_split_keys") - + LOGGER.info({ "target_type": video_type, "transcoded_split_keys": transcoded_split_keys }) - + _, shortname, extension = get_fileNameExt(video_key) segs_filename = 'segs_%s.txt' % (shortname + video_type) segs_filepath = os.path.join(video_process_dir, segs_filename) diff --git a/video-process-flow/src/code/split/index.py b/video-process-flow/src/code/split/index.py index db708ad..686e55f 100644 --- a/video-process-flow/src/code/split/index.py +++ b/video-process-flow/src/code/split/index.py @@ -14,7 +14,7 @@ MAX_SPLIT_NUM = 100 -NAS_ROOT = "/mnt/auto/" +NAS_ROOT = "/mnt/{}/".format(os.environ['FC_FUNCTION_NAME']) class FFmpegError(Exception): def __init__(self, message, status): @@ -65,7 +65,8 @@ def handler(event, context): video_key = evt['video_key'] oss_bucket_name = evt['oss_bucket_name'] segment_time_seconds = str(evt['segment_time_seconds']) - + dst_formats = evt['dst_formats'] + shortname, extension = get_fileNameExt(video_key) video_name = shortname + extension @@ -95,9 +96,31 @@ def handler(event, context): for filename in os.listdir(video_proc_dir): if filename.startswith('split_' + shortname): filekey = os.path.join(video_proc_dir, filename) - split_keys.append(filekey) + split_keys.append( + { + "filekey": filekey + }) + + new_dst_formats = [] + + for df in dst_formats: + + new_sks = [] + for sk in split_keys: + new_sks.append({ + "filekey": sk["filekey"], + "video_proc_dir": video_proc_dir, + "format": df + }) + + new_dst_formats.append( + { + "format": df, + "video_proc_dir": video_proc_dir, + "split_keys": new_sks + }) return { - "split_keys": split_keys, - "video_proc_dir": video_proc_dir + "video_proc_dir": video_proc_dir, + "dst_formats": new_dst_formats } diff --git a/video-process-flow/src/code/transcode/index.py b/video-process-flow/src/code/transcode/index.py index 5a6af3b..38e5285 100644 --- a/video-process-flow/src/code/transcode/index.py +++ b/video-process-flow/src/code/transcode/index.py @@ -58,5 +58,5 @@ def handler(event, context): os.remove(transcoded_filepath) exec_FFmpeg_cmd(['ffmpeg', '-y', '-i', input_path, transcoded_filepath]) - return {} + return evt diff --git a/video-process-flow/src/code/workflows/fc-input.json b/video-process-flow/src/code/workflows/fc-input.json new file mode 100644 index 0000000..7aa5314 --- /dev/null +++ b/video-process-flow/src/code/workflows/fc-input.json @@ -0,0 +1,12 @@ +{ + "oss_bucket_name": "tt0126", + "video_key": "source/fnf-test.mov", + "output_prefix": "outputs", + "segment_time_seconds": 15, + "dst_formats": [ + "mp4", + "flv" + ] +} + +// oss-trigger-workflow 设置 oss触发器,设置前缀为 source,避免无限循环 \ No newline at end of file diff --git a/video-process-flow/src/code/workflows/multimedia-process-workflow.yml b/video-process-flow/src/code/workflows/multimedia-process-workflow.yml new file mode 100644 index 0000000..6cefd91 --- /dev/null +++ b/video-process-flow/src/code/workflows/multimedia-process-workflow.yml @@ -0,0 +1,117 @@ +Type: StateMachine +Name: ${vars.flowName} +SpecVersion: v1 +Description: Serverless workflow video processing +StartAt: Split +States: + - Action: FC:InvokeFunction + Type: Task + TaskMode: RequestComplete + Name: Split + Parameters: + resourceArn: acs:fc:::functions/${vars.flowName}_split + body.$: $Input + Next: ParallelTranscode + OutputConstructor: + split_keys.$: $Output.Body.split_keys + video_proc_dir.$: $Output.Body.video_proc_dir + dst_formats.$: $Output.Body.dst_formats + Retry: + - Errors: + - FC.ResourceThrottled + - FC.ResourceExhausted + - FC.InternalServerError + - FnF.TaskTimeout + - FC.Unknown + IntervalSeconds: 3 + MaxAttempts: 16 + BackoffRate: 2 + - Type: Map + Name: ParallelTranscode + ItemsPath: $Input.dst_formats + Next: after-process + InputConstructor: + split_keys.$: $Input.split_keys + video_proc_dir.$: $Input.video_proc_dir + dst_formats.$: $Input.dst_formats + OutputConstructor: + video_proc_dir.$: $Input.video_proc_dir + Processor: + StartAt: Transcode_splits + States: + - ItemsPath: $Input.split_keys + OutputConstructor: + Items.$: $Output.Items + format.$: $Input.format + split_keys.$: $Input.split_keys + video_proc_dir.$: $Input.video_proc_dir + Name: Transcode_splits + Next: Merge + Processor: + StartAt: Transcode + States: + - Action: FC:InvokeFunction + End: true + TaskMode: RequestComplete + Name: Transcode + Parameters: + resourceArn: acs:fc:::functions/${vars.flowName}_transcode + body: + split_video_key.$: $Input.filekey + target_type.$: $Input.format + video_proc_dir.$: $Input.video_proc_dir + Retry: + - Errors: + - FC.ResourceThrottled + - FC.ResourceExhausted + - FC.InternalServerError + - FnF.TaskTimeout + - FC.Unknown + IntervalSeconds: 3 + MaxAttempts: 16 + BackoffRate: 2 + Type: Task + Type: Map + - Action: FC:InvokeFunction + End: true + Name: Merge + TaskMode: RequestComplete + InputConstructor: + video_key.$: $Context.Execution.Input.video_key + oss_bucket_name.$: $Context.Execution.Input.oss_bucket_name + split_keys.$: $Input.split_keys + output_prefix.$: $Context.Execution.Input.output_prefix + target_type.$: $Input.format + video_proc_dir.$: $Input.video_proc_dir + Parameters: + resourceArn: acs:fc:::functions/${vars.flowName}_merge + body.$: $Input + Retry: + - Errors: + - FC.ResourceThrottled + - FC.ResourceExhausted + - FC.InternalServerError + - FnF.TaskTimeout + - FC.Unknown + IntervalSeconds: 3 + MaxAttempts: 16 + BackoffRate: 2 + Type: Task + - Type: Task + Action: FC:InvokeFunction + TaskMode: RequestComplete + End: true + Name: after-process + Parameters: + resourceArn: acs:fc:::functions/${vars.flowName}_after-process + body.$: $Input + Retry: + - Errors: + - FC.ResourceThrottled + - FC.ResourceExhausted + - FC.InternalServerError + - FnF.TaskTimeout + - FC.Unknown + IntervalSeconds: 3 + MaxAttempts: 16 + BackoffRate: 2 diff --git a/video-process-flow/src/readme.md b/video-process-flow/src/readme.md index f67e4a9..825e53a 100644 --- a/video-process-flow/src/readme.md +++ b/video-process-flow/src/readme.md @@ -1,18 +1,7 @@ -> 注:当前项目为 Serverless Devs 应用,由于应用中会存在需要初始化才可运行的变量(例如应用部署地区、服务名、函数名等等),所以**不推荐**直接 Clone 本仓库到本地进行部署或直接复制 s.yaml 使用,**强烈推荐**通过 `s init ` 的方法或应用中心进行初始化,详情可参考[部署 & 体验](#部署--体验) 。 - -# video-process-flow 帮助文档 -

- - - - - - - - - -

+> 注:当前项目为 Serverless Devs 应用,由于应用中会存在需要初始化才可运行的变量(例如应用部署地区、函数名等等),所以**不推荐**直接 Clone 本仓库到本地进行部署或直接复制 s.yaml 使用,**强烈推荐**通过 `s init ${模版名称}` 的方法或应用中心进行初始化,详情可参考[部署 & 体验](#部署--体验) 。 + +# multimedia-process-flow-v3 帮助文档 @@ -22,7 +11,7 @@ -- [:smiley_cat: 代码](https://github.com/devsapp/start-ffmpeg/tree/master/video-process-flow/src) + @@ -34,38 +23,22 @@ ## 前期准备 -使用该项目,您需要有开通以下服务: +使用该项目,您需要有开通以下服务并拥有对应权限: -| 服务 | 备注 | -| --- | --- | -| 函数计算 FC | 转码等函数部署在函数计算 | -| Serverless 工作流 | 视频处理工作流部署在 Serverless 工作流 | -| 对象存储 OSS | 原视频位于 OSS | -| 文件存储 NAS | 视频临时处理工作区间位于文件存储 NAS | -| 专有网络 VPC | NAS 挂载点需要有 VPC | +| 服务/业务 | 权限 | 相关文档 | +| --- | --- | --- | +| 函数计算 | AliyunFCFullAccess | [帮助文档](https://help.aliyun.com/product/2508973.html) [计费文档](https://help.aliyun.com/document_detail/2512928.html) | +| 硬盘挂载 | AliyunFCServerlessDevsRolePolicy | [帮助文档](https://help.aliyun.com/zh/nas) [计费文档](https://help.aliyun.com/zh/nas/product-overview/billing) | +| 专有网络 | AliyunFCServerlessDevsRolePolicy | [帮助文档](https://help.aliyun.com/zh/vpc) [计费文档](https://help.aliyun.com/zh/vpc/product-overview/billing) | +| 对象存储 | AliyunFCServerlessDevsRolePolicy | [帮助文档](https://help.aliyun.com/zh/oss) [计费文档](https://help.aliyun.com/zh/oss/product-overview/billing) | +| 云工作流 | AliyunFnFFullAccess | [帮助文档](undefined) [计费文档](undefined) | -推荐您拥有以下的产品权限 / 策略: - - - - -| 服务/业务 | 权限 | 备注 | -| --- | --- | --- | -| 函数计算 | AliyunFCFullAccess | 创建或者更新转码等函数 | -| 硬盘挂载 | AliyunNASFullAccess | 视频临时处理工作区间位于文件存储 NAS, 需要有自动创建 NAS 的权限 | -| VPC | AliyunVPCFullAccess | NAS 需要 VPC 挂载点, 需要有 VPC 自动创建的能力 | -| OSS | AliyunOSSFullAccess | 创建 OSS 触发器需要的调用 OSS 相关 API 的权限 | -| 工作流 | AliyunFnFFullAccess | 创建或者更新音视频处理工作流 | -| 其它 | AliyunECSFullAccess | 函数计算 NAS 挂载点需要交换机和安全组, 需要有自动创建的权限 | - - - @@ -82,49 +55,59 @@ -- :fire: 通过 [Serverless 应用中心](https://fcnext.console.aliyun.com/applications/create?template=video-process-flow) , - [![Deploy with Severless Devs](https://img.alicdn.com/imgextra/i1/O1CN01w5RFbX1v45s8TIXPz_!!6000000006118-55-tps-95-28.svg)](https://fcnext.console.aliyun.com/applications/create?template=video-process-flow) 该应用。 +- :fire: 通过 [Serverless 应用中心](https://fcnext.console.aliyun.com/applications/create?template=multimedia-process-flow-v3) , + [![Deploy with Severless Devs](https://img.alicdn.com/imgextra/i1/O1CN01w5RFbX1v45s8TIXPz_!!6000000006118-55-tps-95-28.svg)](https://fcnext.console.aliyun.com/applications/create?template=multimedia-process-flow-v3) 该应用。 -- 通过 [Serverless Devs Cli](https://www.serverless-devs.com/serverless-devs/install) 进行部署: - - [安装 Serverless Devs Cli 开发者工具](https://www.serverless-devs.com/serverless-devs/install) ,并进行[授权信息配置](https://docs.serverless-devs.com/fc/config) ; - - 初始化项目:`s init video-process-flow -d video-process-flow ` - - 进入项目,并进行项目部署:`cd video-process-flow && s deploy - y` +- 通过 [Serverless Devs Cli](https://docs.serverless-devs.com/user-guide/install) 进行部署: + - [安装 Serverless Devs Cli 开发者工具](https://docs.serverless-devs.com/user-guide/install) ,并进行[授权信息配置]( https://docs.serverless-devs.com/user-guide/config) ; + - 初始化项目:`s init multimedia-process-flow-v3 -d multimedia-process-flow-v3` + - 进入项目,并进行项目部署:`cd multimedia-process-flow-v3 && s deploy -y` -## 应用详情 +## 案例介绍 -如下图所示, 假设用户上传一个 mov 格式的视频到 OSS, OSS 触发器自动触发函数执行, 函数调用 FnF 执行,FnF 同时进行 1 种或者多种格式的转码(由 s.yaml 中的 DST_FORMATS 参数控制), 本示例配置的是同时进行 mp4, flv, avi 格式的转码。 +本案例将FFmpeg包装为一款音视频处理工作流,十分适用于加快视频转码处理速度的场景, 基本原理是对每一个视频,先进行切片处理,然后并行转码切片,最后合成,通过设置合理的切片时间,可以大大加速较大视频的转码速度。 -您可以实现如下需求: +FFmpeg 是音视频处理领域的一款强大工具,它可以被用于格式转换、编解码、录制和流处理等多种音视频相关任务。该技术因其高效性和多功能性,在视频处理、直播流媒体、数字媒体播放和编辑等多个领域中得到广泛应用。截至2023年4月,FFmpeg 在 GitHub 上的 star 数接近30,000,这反映了其在开源社区的受欢迎程度和影响力。被Netflix、YouTube、Facebook等多家知名企业采用,这些公司依赖 FFmpeg 处理其庞大的视频数据,以提供流畅的媒体播放体验、视频内容编辑和优化视频传输效率等服务。 -- 一个视频文件可以同时被转码成各种格式以及其他各种自定义处理,比如增加水印处理或者在 after-process 更新信息到数据库等。 + -- 当有多个文件同时上传到 OSS,函数计算会自动伸缩, 并行处理多个文件, 同时每次文件转码成多种格式也是并行。 +## 使用流程 -- 结合 NAS + 视频切片, 可以解决超大视频的转码, 对于每一个视频,先进行切片处理,然后并行转码切片,最后合成,通过设置合理的切片时间,可以大大加速较大视频的转码速度。 + -![image](https://img.alicdn.com/tfs/TB1A.PSzrj1gK0jSZFuXXcrHpXa-570-613.png) +上传一个 mov 格式的视频到对象存储 bucket, OSS 触发器自动触发函数执行,函数调用工作流执行,工作流会同时进行 1 种或者多种格式的转码(由 s.yaml 中的 DST_FORMATS 参数控制), 本示例默认配置的是同时进行 mp4, flv, avi 格式的转码。 - +基于该工作流,您可以实现如下需求: -## 使用文档 +1 一个视频文件可以同时被转码成各种格式以及其他各种自定义处理,比如增加水印处理或者在 after-process 更新信息到数据库等。 + +2 当有多个文件同时上传到 OSS,函数计算会自动伸缩, 并行处理多个文件, 同时每次文件转码成多种格式也是并行。 + +3 结合 NAS + 视频切片, 可以解决超大视频的转码, 对于每一个视频,先进行切片处理,然后并行转码切片,最后合成,通过设置合理的切片时间,可以大大加速较大视频的转码速度。 + +![image](https://img.alicdn.com/tfs/TB1A.PSzrj1gK0jSZFuXXcrHpXa-570-613.png) - **操作视频教程:** -[![Watch the video](https://img.alicdn.com/imgextra/i2/O1CN01XvnqJu1XLS8SAU7LT_!!6000000002907-2-tps-250-155.png)](http://devsapp.functioncompute.com/video/video-process-flow.mp4) +[![Watch the video](https://img.alicdn.com/imgextra/i2/O1CN01XvnqJu1XLS8SAU7LT_!!6000000002907-2-tps-250-155.png)](http://devsapp.functioncompute.com/video/multimedia-process-flow.mp4) -**P.S.** 当您想要仅在一个简单的函数中直接完成视频处理逻辑时,可以参考[音视频转码 Job](https://github.com/devsapp/start-ffmpeg/tree/master/transcode) +**P.S.** 当您想要仅在一个简单的函数中直接完成视频处理逻辑时,可以参考[音视频转码 Job](https://github.com/devsapp/start-ffmpeg/tree/V3/transcode/src) +## 注意事项 + + + + @@ -135,7 +118,7 @@

-| | | | +| | | | | --------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | |

微信公众号:`serverless`
|
微信小助手:`xiaojiangwh`
|
钉钉交流群:`33947367`
|

diff --git a/video-process-flow/src/s.yaml b/video-process-flow/src/s.yaml index b5db1fa..2548041 100644 --- a/video-process-flow/src/s.yaml +++ b/video-process-flow/src/s.yaml @@ -1,124 +1,105 @@ -# ------------------------------------ -# 欢迎您使用阿里云函数计算 FC 组件进行项目开发 -# 组件仓库地址:https://github.com/devsapp/fc -# 组件帮助文档:https://www.serverless-devs.com/fc/readme -# Yaml参考文档:https://www.serverless-devs.com/fc/yaml/readme -# 关于: -# - Serverless Devs和FC组件的关系、如何声明/部署多个函数、超过50M的代码包如何部署 -# - 关于.fcignore使用方法、工具中.s目录是做什么、函数进行build操作之后如何处理build的产物 -# 等问题,可以参考文档:https://www.serverless-devs.com/fc/tips -# 关于如何做CICD等问题,可以参考:https://www.serverless-devs.com/serverless-devs/cicd -# 关于如何进行环境划分等问题,可以参考:https://www.serverless-devs.com/serverless-devs/extend -# 更多函数计算案例,可参考:https://github.com/devsapp/awesome/ -# 有问题快来钉钉群问一下吧:33947367 -# ------------------------------------ - -edition: 1.0.0 -name: video-process-flow -# access 是当前应用所需要的密钥信息配置: -# 密钥配置可以参考:https://www.serverless-devs.com/serverless-devs/command/config -# 密钥使用顺序可以参考:https://www.serverless-devs.com/serverless-devs/tool#密钥使用顺序与规范 -access: "{{ access }}" - +edition: 3.0.0 +name: multimedia-process-flow-v3 +access: '{{ access }}' vars: - region: "{{ region }}" - service: - name: "{{ serviceName }}" - description: use fc+fnf+ffmpeg to transcode video in FC - internetAccess: true - role: "{{ serviceRoleArn }}" - nasConfig: auto - # logConfig: auto - flowName: "{{ flowName }}" + region: '{{ region }}' + flowName: '{{ flowName }}' + role: '{{ functionRoleArn }}' -services: - # 函数计算配置 - fc-video-demo-split: - component: devsapp/fc +resources: + split: + component: fc3 props: region: ${vars.region} - service: ${vars.service} - function: - name: split - handler: index.handler - timeout: 600 - memorySize: 3072 - runtime: python3 - codeUri: code/split - fc-video-demo-transcode: - component: devsapp/fc + handler: index.handler + timeout: 600 + memorySize: 3072 + runtime: python3 + functionName: ${vars.flowName}_split + role: ${vars.role} + code: code/split + vpcConfig: auto + nasConfig: auto + + + transcode: + component: fc3 props: region: ${vars.region} - service: ${vars.service} - function: - name: transcode - handler: index.handler - timeout: 600 - memorySize: 3072 - runtime: python3 - codeUri: code/transcode - fc-video-demo-merge: - component: devsapp/fc + handler: index.handler + timeout: 600 + memorySize: 3072 + runtime: python3 + functionName: ${vars.flowName}_transcode + role: ${vars.role} + code: code/transcode + vpcConfig: ${resources.split.output.vpcConfig} + nasConfig: ${resources.split.output.nasConfig} + + merge: + component: fc3 props: region: ${vars.region} - service: ${vars.service} - function: - name: merge - handler: index.handler - timeout: 600 - memorySize: 3072 - runtime: python3 - codeUri: code/merge - fc-video-demo-after-process: - component: devsapp/fc + handler: index.handler + timeout: 600 + memorySize: 3072 + runtime: python3 + functionName: ${vars.flowName}_merge + role: ${vars.role} + code: code/merge + vpcConfig: ${resources.split.output.vpcConfig} + nasConfig: ${resources.split.output.nasConfig} + + after-process: + component: fc3 props: region: ${vars.region} - service: ${vars.service} - function: - name: after-process - handler: index.handler - timeout: 120 - memorySize: 512 - runtime: python3 - codeUri: code/after-process - fc-oss-trigger-trigger-fnf: - component: devsapp/fc + handler: index.handler + timeout: 120 + memorySize: 512 + runtime: python3 + functionName: ${vars.flowName}_after-process + role: ${vars.role} + code: code/after-process + vpcConfig: ${resources.split.output.vpcConfig} + nasConfig: ${resources.split.output.nasConfig} + + oss-trigger-workflow: + component: fc3 props: region: ${vars.region} - service: ${vars.service} - function: - name: trigger-fnf - handler: index.handler - timeout: 120 - memorySize: 128 - runtime: python3 - codeUri: code/oss-trigger - environmentVariables: - OUTPUT_DST: '{{ outputDir }}' - FLOW_NAME: ${vars.flowName} - SEG_INTERVAL: '{{ segInterval }}' - DST_FORMATS: '{{ dstFormats }}' + handler: index.handler + timeout: 120 + memorySize: 128 + runtime: python3 + environmentVariables: + OUTPUT_DST: '{{ outputDir }}' + FLOW_NAME: ${vars.flowName} + SEG_INTERVAL: '{{ segInterval }}' + DST_FORMATS: '{{ dstFormats }}' + functionName: ${vars.flowName}_oss-trigger-workflow + role: ${vars.role} + code: code/oss-trigger triggers: - - name: oss-t - type: oss - role: '{{ triggerRoleArn }}' - config: + - triggerName: ${vars.flowName} + triggerType: oss + triggerConfig: events: - oss:ObjectCreated:PutObject - oss:ObjectCreated:PostObject - oss:ObjectCreated:CompleteMultipartUpload filter: - Key: - Prefix: '{{ prefix }}' - Suffix: '' - bucketName: '{{ ossBucket }}' + key: + prefix: '{{ prefix }}' + suffix: '' + invocationRole: '{{ triggerRoleArn }}' + sourceArn: acs:oss:${this.props.region}:${config("AccountID")}:{{ ossBucket }} - # fnf 服务配置 - video-demo-flow: - component: devsapp/fnf + multimedia-process-v3-workflow: + component: flow props: name: ${vars.flowName} region: ${vars.region} - description: FnF video processing demo flow - definition: code/flows/video-processing-fc.yml - roleArn: "{{ fnfRoleArn }}" \ No newline at end of file + description: Serverless workflow video process + definition: ${yaml(file('code/workflows/multimedia-process-workflow.yml'))} + roleArn: '{{ fnfRoleArn }}'