在音視頻系統中,音視頻轉碼是比較消耗計算力的一個子系統,您可以通過函數計算和Serverless 工作流構建彈性高可用的Serverless音視頻處理系統。本文會從工程效率、運維、性能和成本方面介紹Serverless音視頻處理系統和傳統方案的差異。同時介紹如何搭建并體驗Serverless音視頻處理系統。
背景信息
針對音視頻轉碼,雖然您可以使用云上專門的轉碼服務,但在以下場景,您仍然會選擇自己搭建轉碼服務。
彈性伸縮訴求
需要更彈性的視頻處理服務。
例如,您已經在虛擬機或容器平臺上基于FFmpeg部署了一套視頻處理服務,但想在此基礎上實現更彈性、更高可用性的服務。
工程效率訴求
需要并行處理多個視頻文件。
需要批量快速處理多個超大的視頻。
例如,每周五定時產生幾百個4 GB以上1080P的大視頻,需要幾小時內處理完。
自定義處理訴求
需要處理更高級的自定義處理需求。
例如,視頻轉碼完成后,需要將轉碼詳情記錄到數據庫,或自動將熱度很高的視頻預熱到CDN上緩解源站壓力。
需要轉換音頻格式、自定義采樣率或音頻降噪等。
需要直接讀取源文件進行處理。
例如,當您的視頻源文件存放在NAS或ECS云盤上時,您需要自建服務直接讀取源文件進行處理,而不需要將它們再次遷移至OSS。
需要將視頻轉換為其他格式,然后在此基礎上增加其他新的需求。
例如,將視頻進行轉碼、加水印和生成視頻首頁的GIF,然后在此基礎上增加新的需求,例如調整轉碼參數、發布新功能時對在線服務無影響。
成本訴求
需要簡單的轉碼或較輕量的需求時。
例如,獲取OSS上視頻前幾幀的GIF、獲取音視頻的時長,此時您自己搭建的成本會更低。
針對自己搭建轉碼服務的方案有傳統自建方案和Serverless方案,本文會介紹這兩類方案的差異及Serverless方案具體的操作流程。
解決方案
傳統自建方案
隨著計算機技術和網絡的發展,云計算廠商的產品在不斷成熟完善。如果想要搭建音視頻處理系統,可以直接上云購買ECS服務器部署服務,通過OSS、CDN等技術完成音視頻的存儲和音視頻的播放加速。
Serverless方案
簡單視頻處理系統
如果您只需要對視頻進行簡單的處理,方案架構圖如下。
當用戶上傳一個視頻到OSS時,OSS觸發器自動觸發函數執行,函數調用FFmpeg進行視頻轉碼,并且將轉碼后的視頻保存回OSS。
關于簡單視頻處理系統的Demo及操作步驟,請參見簡單視頻處理系統。
視頻處理工作流系統
如果您需要加快大視頻的轉碼速度或者完成各種復雜的組合操作,可以通過Serverless 工作流編排函數實現功能強大的視頻處理系統。方案架構圖如下。
當用戶上傳一個MP4格式的視頻到OSS,OSS觸發器自動觸發函數執行,然后函數調用Serverless 工作流進行一種或多種格式的轉碼。通過該方案可以實現以下需求:
一個視頻文件可以同時被轉碼成各種格式和進行其他自定義處理,例如增加水印或在處理后更新信息到數據庫等。
當有多個文件同時上傳到OSS時,函數計算會自動伸縮,并行處理多個文件,同時將文件并行轉碼成多種格式。
結合NAS和視頻切片,可以解決超大視頻的轉碼問題。對于每個視頻,都需先進行切片處理,然后并行轉碼切片,最后合成。通過設置合理的切片時間,可以提高較大視頻的轉碼速度。
說明視頻切片是指將視頻流按指定的時間間隔切分成一系列分片文件,并生成一個索引文件記錄分片文件的信息。
關于視頻處理工作流系統的Demo及操作步驟,請參見視頻處理工作流系統。
Serverless方案優勢
提高工程效率
對比項 | Serverless方案 | 傳統自建方案 |
基礎設施 | 無 | 需要您采購和管理。 |
開發效率 | 只需專注業務邏輯的開發,配合Serverless Devs編排和部署資源。 | 除了必要的業務邏輯開發,還需要您自己建立相同的線上運行環境,包括相關軟件的安裝、服務配置、安全更新等一系列任務。 |
并行和分布式視頻處理 | 通過Serverless 工作流資源編排即可實現多個視頻的并行處理和單個大視頻的分布式處理,穩定性和監控交由云平臺。 | 需要很強的開發能力和完善的監控系統來保證穩定性。 |
學習上手成本 | 會編寫對應語言的函數代碼和熟悉FFmpeg使用即可。 | 除了編程語言開發能力和熟悉FFmpeg外,可能還需要使用K8s和云服務器ECS,需要了解更多的產品、名詞和參數意義。 |
項目上線周期 | 預計3人天(開發調試2人天和壓測觀察1人天)。 | 在具體業務邏輯外耗費大量的時間和人力成本,保守估計大約30人天,包括硬件采購、軟件和環境配置、系統開發、測試、監控報警和灰度發布等。 |
彈性伸縮免運維
內容項 | Serverless方案 | 傳統自建方案 |
彈性高可用 | 函數計算系統具有毫秒級彈性伸縮,可以快速實現底層擴容以應對峰值壓力,免運維,轉碼性能優異。 | 需要自建負載均衡SLB,彈性伸縮、擴容縮容速度較函數計算慢。 |
監控報警查詢 | 提供更細粒度的Serverless 工作流流程執行和函數執行情況。同時,可以查詢每次函數執行的Latency和日志等,更加完善的報警監控機制。 | 彈性伸縮或容器級別的Metrics。 |
轉碼性能優勢
假設視頻為89s的MOV格式的文件,云服務將MOV轉為MP4的普通轉碼需要消耗的時間為188s,將這個參考時間記為T。性能加速百分比計算公式為:性能加速百分比=T÷函數計算轉碼耗時
。
視頻切片時間(s) | 函數計算轉碼耗時(s) | 性能加速百分比(%) |
45 | 160 | 117.5 |
25 | 100 | 188 |
15 | 70 | 268.6 |
10 | 45 | 417.8 |
5 | 35 | 537.1 |
上表中的性能加速百分比僅適用于CPU實例,當您的示例場景是GPU實例時,請參見音視頻處理最佳實踐。
成本低
在某些實際場景中,函數計算在視頻處理上的成本更低。即使和云廠商視頻轉碼服務單價對比,本方案仍有很強的成本競爭力。
下文中選用點播視頻中最常用的兩個格式MP4和FLV之間進行相互轉換,函數內存設置為3 GB,經實驗驗證,基于該方案MP4與FLV相互轉換的費用如下表所示。
成本下降百分比公式為:成本下降百分比=(某云視頻處理費用-函數計算轉碼費用)÷某云視頻處理費用
。
表 1. MP4轉FLV
分辨率 | 速率 | 幀率 | 函數計算轉碼耗費時間 | 函數計算轉碼費用 | 某云視頻處理費用 | 成本下降百分比 |
標清640*480 | 889 KB/s | 24 | 11.2s | 0.003732288 | 0.032 | 88.3% |
高清1280*720 | 1963 KB/s | 24 | 20.5s | 0.00683142 | 0.065 | 89.5% |
超清1920*1080 | 3689 KB/s | 24 | 40s | 0.0133296 | 0.126 | 89.4% |
4K 3840*2160 | 11185 KB/s | 24 | 142s | 0.04732008 | 0.556 | 91.5% |
表 2. FLV轉MP4
分辨率 | 速率 | 幀率 | 函數計算轉碼耗費時間 | 函數計算轉碼費用 | 某云視頻處理費用 | 成本下降百分比 |
標清640*480 | 712 KB/s | 24 | 34.5s | 0.01149678 | 0.032 | 64.1% |
高清1280*720 | 1806 KB/s | 24 | 100.3s | 0.033424 | 0.065 | 48.6% |
超清1920*1080 | 3911 KB/s | 24 | 226.4s | 0.0754455 | 0.126 | 40.1% |
4K 3840*2160 | 15109 KB/s | 24 | 912s | 0.30391488 | 0.556 | 45.3% |
某云視頻處理計費使用普通轉碼,轉碼時長不足一分鐘,按照一分鐘計算,這里計費采用的是2分鐘,即使采用1.5分鐘計算,成本下降百分比基本在10%以內浮動。
從上表可以看出,基于函數計算和Serverless 工作流的方案在計算資源成本上對于計算復雜度較高的FLV格式轉MP4格式還是計算復雜度較低的MP4格式轉FLV格式,都具有很強的成本競爭力。根據實際經驗,往往成本下降比上表列出來的更加明顯,理由如下:
測試視頻的碼率較高,實際上很多場景絕大部分都是標清或流暢視頻的轉碼場景,碼率也比測試視頻低,此時計算量變小,函數計算執行時間短,費用會降低,但是通用的云轉碼服務計費是不變的。
很多視頻分辨率在通用的云轉碼服務時計費是有很大損失的,例如轉碼的視頻是856*480或1368*768,都會進入云轉碼服務的下一檔計費單價,即856*480進入1280*720高清轉碼計費檔,1368*768進入1920*1080超清轉碼計費檔,單價基本是跨越式上升,但是實際真正的計算量增加可能不到30%,而函數計算則是真正能做到按量付費的。
操作部署
本文會介紹Serverless方案中簡單視頻處理系統和視頻處理工作流系統兩種場景的部署方案。
簡單視頻處理系統
前提條件
操作步驟
創建服務。
創建函數。
在函數管理頁面,單擊創建函數。
在創建函數頁面,按需選擇創建函數的方式,配置以下配置項,然后單擊創建。
創建函數方式:使用內置運行時創建。
基本設置:配置函數的基本信息,包括函數名稱和請求處理程序類型。請求處理程序類型選擇處理事件請求。
函數代碼:函數的運行環境選擇Python 3.9,代碼上傳方式選擇使用示例代碼。
高級配置:由于處理視頻文件比較耗時,并且要考慮視頻文件的大小,所以本文選擇vCPU規格為4核,內存規格為8 GB,臨時硬盤大小為10 GB,執行超時時間為7200秒。
其他配置項使用默認值,關于創建函數的詳細配置信息,請參見創建函數。
創建OSS觸發器。
在函數詳情頁面,單擊觸發器管理頁簽,從版本或別名下拉列表選擇要創建觸發器的版本或別名,然后單擊創建觸發器。
在創建觸發器面板,填寫相關信息。然后單擊確定。
配置項
操作
本文示例
觸發器類型
選擇對象存儲 OSS。
對象存儲 OSS
名稱
填寫自定義的觸發器名稱。
oss-trigger
版本或別名
默認值為LATEST,如果您需要創建其他版本或別名的觸發器,需先在函數詳情頁的版本或別名下拉列表選擇該版本。關于版本和別名的簡介,請參見管理版本和管理別名。
LATEST
Bucket 名稱
選擇已創建的OSS Bucket。
testbucket
文件前綴
輸入要匹配的文件名稱的前綴。建議您配置文件的前綴和后綴,避免觸發事件嵌套循環觸發引起額外費用。此外,一個Bucket的不同觸發器如果指定了相同的事件類型,則前綴和后綴不能重復。詳細信息,請參見OSS觸發器觸發規則。
重要文件前綴不能以
/
開頭,否則會導致OSS觸發器無法被觸發。source
文件后綴
輸入要匹配的文件名稱的后綴。強烈建議您配置前綴和后綴,避免觸發事件嵌套循環觸發引起額外費用。另外,一個Bucket的不同觸發器如果指定了相同的事件類型,則前綴和后綴不能重復。詳細信息,請參見OSS觸發器觸發規則。
mp4
觸發事件
選擇一個或多個觸發事件。關于對象存儲OSS的事件類型,請參見OSS事件定義。
oss:ObjectCreated:PutObject, oss:ObjectCreated:PostObject, oss:ObjectCreated:CompleteMultipartUpload
角色名稱
選擇AliyunOSSEventNotificationRole。
說明如果您第一次創建該類型的觸發器,則需要在單擊確定后,在彈出的對話框中選擇立即授權。
AliyunOSSEventNotificationRole
編寫函數代碼。
在函數詳情頁面,單擊函數代碼頁簽,在代碼編輯器中編寫代碼。
函數需要將MP4格式的視頻文件轉為FLV格式,并將FLV格式視頻文件保存在OSS Bucket下的
dest
目錄。本文以Python語言為例,示例代碼如下。# -*- coding: utf-8 -*- import logging import oss2 import os import json import subprocess import shutil logging.getLogger("oss2.api").setLevel(logging.ERROR) logging.getLogger("oss2.auth").setLevel(logging.ERROR) LOGGER = logging.getLogger() def get_fileNameExt(filename): (_, tempfilename) = os.path.split(filename) (shortname, extension) = os.path.splitext(tempfilename) return shortname, extension def handler(event, context): LOGGER.info(event) evt = json.loads(event) evt = evt["events"] oss_bucket_name = evt[0]["oss"]["bucket"]["name"] object_key = evt[0]["oss"]["object"]["key"] output_dir = "dest" dst_format = "flv" shortname, _ = get_fileNameExt(object_key) creds = context.credentials auth = oss2.StsAuth(creds.accessKeyId, creds.accessKeySecret, creds.securityToken) oss_client = oss2.Bucket(auth, 'oss-%s-internal.aliyuncs.com' % context.region, oss_bucket_name) exist = oss_client.object_exists(object_key) if not exist: raise Exception("object {} is not exist".format(object_key)) input_path = oss_client.sign_url('GET', object_key, 6 * 3600) # m3u8 特殊處理 rid = context.request_id if dst_format == "m3u8": return handle_m3u8(rid, oss_client, input_path, shortname, output_dir) else: return handle_common(rid, oss_client, input_path, shortname, output_dir, dst_format) def handle_m3u8(request_id, oss_client, input_path, shortname, output_dir): ts_dir = '/tmp/ts' if os.path.exists(ts_dir): shutil.rmtree(ts_dir) os.mkdir(ts_dir) transcoded_filepath = os.path.join('/tmp', shortname + '.ts') split_transcoded_filepath = os.path.join( ts_dir, shortname + '_%03d.ts') cmd1 = ['ffmpeg', '-y', '-i', input_path, '-c:v', 'libx264', transcoded_filepath] cmd2 = ['ffmpeg', '-y', '-i', transcoded_filepath, '-c', 'copy', '-map', '0', '-f', 'segment', '-segment_list', os.path.join(ts_dir, 'playlist.m3u8'), '-segment_time', '10', split_transcoded_filepath] try: subprocess.run( cmd1, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True) subprocess.run( cmd2, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True) for filename in os.listdir(ts_dir): filepath = os.path.join(ts_dir, filename) filekey = os.path.join(output_dir, shortname, filename) oss_client.put_object_from_file(filekey, filepath) os.remove(filepath) print("Uploaded {} to {}".format(filepath, filekey)) except subprocess.CalledProcessError as exc: # if transcode fail,trigger invoke dest-fail function raise Exception(request_id + " transcode failure, detail: " + str(exc)) finally: if os.path.exists(ts_dir): shutil.rmtree(ts_dir) # remove ts 文件 if os.path.exists(transcoded_filepath): os.remove(transcoded_filepath) return {} def handle_common(request_id, oss_client, input_path, shortname, output_dir, dst_format): transcoded_filepath = os.path.join('/tmp', shortname + '.' + dst_format) if os.path.exists(transcoded_filepath): os.remove(transcoded_filepath) cmd = ["ffmpeg", "-y", "-i", input_path, transcoded_filepath] try: subprocess.run( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True) oss_client.put_object_from_file( os.path.join(output_dir, shortname + '.' + dst_format), transcoded_filepath) except subprocess.CalledProcessError as exc: # if transcode fail,trigger invoke dest-fail function raise Exception(request_id + " transcode failure, detail: " + str(exc)) finally: if os.path.exists(transcoded_filepath): os.remove(transcoded_filepath) return {}
單擊部署代碼。
測試函數代碼。
您可以通過配置函數入口參數模擬OSS事件參數進行測試,驗證代碼的正確性。在實際操作過程中,當發生OSS事件時,函數會自動被觸發執行。
在函數詳情頁面,單擊函數代碼頁簽,然后單擊測試函數右側圖標,從下拉列表中,選擇配置測試參數。
在配置測試參數面板,選擇創建新測試事件或編輯已有測試事件頁簽,填寫事件名稱和事件內容。然后單擊確定。
本文配置的事件內容如下,關于event參數具體信息,請參見步驟二:配置函數入口參數。
{ "events": [ { "eventName": "oss:ObjectCreated:CompleteMultipartUpload", "eventSource": "acs:oss", "eventTime": "2022-08-13T06:45:43.000Z", "eventVersion": "1.0", "oss": { "bucket": { "arn": "acs:oss:cn-hangzhou:123456789:testbucket", "name": "testbucket", "ownerIdentity": "164901546557****" }, "object": { "deltaSize": 122539, "eTag": "688A7BF4F233DC9C88A80BF985AB****", "key": "source/a.mp4", "size": 122539 }, "ossSchemaVersion": "1.0", "ruleId": "9adac8e253828f4f7c0466d941fa3db81161****" }, "region": "cn-hangzhou", "requestParameters": { "sourceIPAddress": "140.205.XX.XX" }, "responseElements": { "requestId": "58F9FF2D3DF792092E12044C" }, "userIdentity": { "principalId": "164901546557****" } } ] }
單擊測試函數,在函數代碼頁簽查看執行結果。
視頻處理工作流系統
前提條件
開通服務并創建Bucket
函數計算:開通函數計算服務。
對象存儲OSS:控制臺創建存儲空間。
Serverless 工作流:開通Serverless工作流。
文件存儲 NAS:開通文件存儲服務NAS。
專有網絡 VPC:開通專有網絡VPC。
配置服務角色
AliyunFcDefaultRole:創建服務時需要配置該角色,同時該角色需要配置權限
AliyunOSSFullAccess
、AliyunFnFFullAccess
、AliyunFCInvocationAccess
,用于調用函數、管理工作流和管理對象存儲OSS。AliyunOSSEventNotificationRole:OSS默認使用此角色發送事件通知。
fnf-execution-default-role:創建及管理工作流所需要的角色,該角色需要配置權限
AliyunFCInvocationAccess
、AliyunFnFFullAccess
。
Serverless Devs工具安裝及配置
操作步驟
該方案需要通過Serverless工作流編排函數實現視頻處理系統,涉及到多個函數代碼和工作流的配置和編寫。本文使用Serverless Devs部署該系統。
執行以下命令,初始化應用。
s init video-process-flow -d video-process-flow
在執行過程中,需要配置的信息如下。您可以根據實際情況配置對應內容。
配置項
示例
地域
cn-hangzhou
服務名
video-process-flow-demo
函數計算Service RAM角色ARN
acs:ram::10343546****:role/aliyunfcdefaultrole
對象存儲存儲桶名
testBucket
前綴
source
轉碼后的視頻保存目錄
dest
OSS觸發器RAM角色ARN
acs:ram::10343546****:role/aliyunosseventnotificationrole
對視頻進行分片處理的分片時間
30
轉碼后的視頻格式
mp4, flv, avi
工作流程名稱
video-process-flow
工作流RAM角色ARN
acs:ram::10343546****:role/fnf-execution-default-role
please select credential alias
default
執行以下命令,進入項目并進行項目部署。
cd video-process-flow && s deploy - y
部署成功輸出內容如下:
[2023-08-31 13:22:21] [INFO] [S-CORE] - Project video-demo-flow successfully to execute fc-video-demo-split: region: cn-hangzhou service: name: video-process-flow-wg76 function: name: split runtime: python3 handler: index.handler memorySize: 3072 timeout: 600 fc-video-demo-transcode: region: cn-hangzhou service: name: video-process-flow-wg76 function: name: transcode runtime: python3 handler: index.handler memorySize: 3072 timeout: 600 fc-video-demo-merge: region: cn-hangzhou service: name: video-process-flow-wg76 function: name: merge runtime: python3 handler: index.handler memorySize: 3072 timeout: 600 fc-video-demo-after-process: region: cn-hangzhou service: name: video-process-flow-wg76 function: name: after-process runtime: python3 handler: index.handler memorySize: 512 timeout: 120 fc-oss-trigger-trigger-fnf: region: cn-hangzhou service: name: video-process-flow-wg76 function: name: trigger-fnf runtime: python3 handler: index.handler memorySize: 128 timeout: 120 triggers: - type: oss name: oss-t video-demo-flow: RegionId: cn-hangzhou Name: video-process-flow
測試項目。
登錄OSS管理控制臺。然后進入testBucket的source目錄,上傳一個MP4視頻文件。
登錄Serverless工作流控制臺,在流程管理頁面,單擊目標工作流,在執行頁簽下,單擊目標執行名稱,可以查看工作流執行流程及執行狀態。
當工作執行狀態為已成功時,可以進入testBucket的dest目錄,查看轉碼后的文件。
當看到轉碼后的文件時,則說明視頻處理系統的服務已經正常運行。
相關文檔
OSS觸發器
Serverless Devs
常見問題
如果已經在虛擬機或容器平臺上基于FFmpeg部署了一套視頻處理服務,能否可以在此基礎上增加其彈性,擁有更高的可用性?
如本文所示,在虛擬機或容器平臺上基于FFmpeg的服務可以輕松切換到函數計算,FFmpeg相關命令可以直接移植到函數計算,改造成本較低,同時,繼承函數計算彈性高可用性的特性。
有并發處理大量視頻的需求時,如何操作?
部署方案,請參見視頻處理工作流系統。當有多個文件同時上傳到OSS時,函數計算會自動伸縮,并行處理多個文件。更多信息,請參見視頻處理工作流系統壓測。
有很多超大的視頻需要批量快速處理完,例如每周五定期產生幾百個4 GB以上的1080P大視頻,需要在當天幾小時內全部處理完時,如何操作?
可以通過控制分片的大小,使每個大視頻都有足夠多的計算資源參與轉碼計算,從而大大提高轉碼速度。部署方案,請參見視頻處理工作流系統壓測。
有更高級的自定義處理需求,例如視頻轉碼完成后,需要記錄轉碼詳情到數據庫,或在轉碼完成后,自動將熱度很高的視頻預熱到CDN上,從而緩解源站壓力時,如何操作?
部署方案,請參見視頻處理工作流系統。處理中可以做一些自定義的操作,或基于此流程再做一些額外處理等,例如再增加后續流程,或最開始增加預處理等。
自定義視頻處理流程中可能會有多種操作組合,例如轉碼、加水印和生成視頻首頁GIF。如果需要后續為視頻處理系統增加新需求,例如調整轉碼參數,并且希望新功能發布上線對在線服務無影響時,如何操作?
部署方案,請參見視頻處理工作流系統。Serverless 工作流只負責編排調用函數,因此只需要更新相應的處理函數即可,同時函數有版本和別名功能,更好地控制灰度上線。更多信息,請參見版本。
只有簡單的轉碼需求,或是一些極其輕量的需求,如需獲取OSS上視頻前幾幀的GIF、獲取音視頻的時長,自己搭建成本更低時,如何操作?
函數計算可以解決自定義問題,只需要在代碼中快速執行幾個FFmpeg的命令即可完成需求。典型示例工程,請參見fc-oss-ffmpeg。
視頻源文件存放在NAS或ECS云盤上,希望自建服務可以直接讀取源文件處理,而不需要將它們再遷移到OSS時,如何操作?
函數計算可以掛載NAS,直接處理NAS中的文件。更多信息,請參見配置NAS文件系統。